We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[cirqflow] `KeyValueExecutableSpec` should provide a `to_dict` method / override `__getitem__`
**Is your feature request related to a use case or problem? Please describe.**
`cg.KeyValueExecutableSpec` provides a nice `from_dict()` method to convert a dict into a `Tuple[Tuple[str, Any], ...]` which is hashable. This is useful when constructing the executable spec. However, using the executable spec during analysis of the results forces one to use the stored tuples, which is cumbersome.

**Describe the solution you'd like**
The class should provide a similar `to_dict` method which can convert the stored `key_value_pairs` to a dictionary and return -- which are much easier to work with.  Though the method would be a simple `return dict(self.key_value_pairs)`, there might be some value in explicitly having it on the class.  We can also consider providing a custom `__getitem__` method. 

**What is the urgency from your perspective for this issue? Is it blocking important work?**
P1 - I need this no later than the next release (end of quarter)

cc @mpharrigan 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cirq-google/cirq_google/workflow/quantum_executable.py`
Content:
```
1 # Copyright 2021 The Cirq Developers
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     https://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """Data structures for programs executable on a quantum runtime."""
16 
17 import abc
18 import dataclasses
19 from dataclasses import dataclass
20 from typing import Union, Tuple, Optional, Sequence, cast, Dict, Any, List, Iterator
21 
22 import cirq
23 from cirq import _compat, study
24 
25 
26 class ExecutableSpec(metaclass=abc.ABCMeta):
27     """Specification metadata about an executable.
28 
29     Subclasses should add problem-specific fields.
30     """
31 
32     executable_family: str = NotImplemented
33     """A unique name to group executables."""
34 
35 
36 @dataclass(frozen=True)
37 class KeyValueExecutableSpec(ExecutableSpec):
38     """A generic executable spec whose metadata is a list of key-value pairs.
39 
40     The key-value pairs define an implicit data schema. Consider defining a problem-specific
41     subclass of `ExecutableSpec` instead of using this class to realize the benefits of having
42     an explicit schema.
43 
44     See Also:
45         `KeyValueExecutableSpec.from_dict` will use a dictionary to populate `key_value_pairs`.
46 
47     Args:
48         executable_family: A unique name to group executables.
49         key_value_pairs: A tuple of key-value pairs. The keys should be strings but the values
50             can be any immutable object.
51     """
52 
53     executable_family: str
54     key_value_pairs: Tuple[Tuple[str, Any], ...] = ()
55 
56     @classmethod
57     def _json_namespace_(cls) -> str:
58         return 'cirq.google'
59 
60     def _json_dict_(self) -> Dict[str, Any]:
61         return cirq.dataclass_json_dict(self)
62 
63     @classmethod
64     def from_dict(cls, d: Dict[str, Any], *, executable_family: str) -> 'KeyValueExecutableSpec':
65         return cls(
66             executable_family=executable_family,
67             key_value_pairs=tuple((k, v) for k, v in d.items()),
68         )
69 
70     @classmethod
71     def _from_json_dict_(
72         cls, executable_family: str, key_value_pairs: List[List[Union[str, Any]]], **kwargs
73     ) -> 'KeyValueExecutableSpec':
74         return cls(
75             executable_family=executable_family,
76             key_value_pairs=tuple((k, v) for k, v in key_value_pairs),
77         )
78 
79     def __repr__(self) -> str:
80         return cirq._compat.dataclass_repr(self, namespace='cirq_google')
81 
82 
83 @dataclass(frozen=True)
84 class BitstringsMeasurement:
85     """Use in-circuit MeasurementGate to collect many repetitions of strings of bits.
86 
87     This is the lowest-level measurement type allowed in `QuantumExecutable` and behaves
88     identically to the `cirq.Sampler.run` function. The executable's circuit must contain
89     explicit measurement gates.
90 
91     Args:
92         n_repeitions: The number of repetitions to execute the circuit.
93     """
94 
95     n_repetitions: int
96 
97     @classmethod
98     def _json_namespace_(cls) -> str:
99         return 'cirq.google'
100 
101     def _json_dict_(self):
102         return cirq.dataclass_json_dict(self)
103 
104     def __repr__(self):
105         return cirq._compat.dataclass_repr(self, namespace='cirq_google')
106 
107 
108 TParamPair = Tuple[cirq.TParamKey, cirq.TParamVal]
109 
110 
111 @dataclass(frozen=True)
112 class QuantumExecutable:
113     """An executable quantum program.
114 
115     This serves a similar purpose to `cirq.Circuit` with some key differences. First, a quantum
116     executable contains all the relevant context for execution including parameters as well as
117     the desired number of repetitions. Second, this object is immutable. Finally, there are
118     optional fields enabling a higher level of abstraction for certain aspects of the executable.
119 
120     Attributes:
121         circuit: A `cirq.Circuit` describing the quantum operations to execute.
122         measurement: A description of the measurement properties or process.
123         params: An immutable `cirq.ParamResolver` (or similar type). It's representation is
124             normalized to a tuple of key value pairs.
125         spec: Optional `cg.ExecutableSpec` containing metadata about this executable that is not
126             used by the quantum runtime, but will be forwarded to all downstream result objects.
127         problem_topology: Optional `cirq.NamedTopology` instance specifying the topology of the
128             circuit. This is useful when optimizing on-device layout. If none is provided we
129             assume `circuit` already has a valid on-device layout.
130         initial_state: A `cirq.ProductState` specifying the desired initial state before executing
131             `circuit`. If not specified, default to the all-zeros state.
132     """
133 
134     circuit: cirq.FrozenCircuit
135     measurement: BitstringsMeasurement
136     params: Optional[Tuple[TParamPair, ...]] = None
137     spec: Optional[ExecutableSpec] = None
138     problem_topology: Optional[cirq.NamedTopology] = None
139     initial_state: Optional[cirq.ProductState] = None
140 
141     # pylint: disable=missing-raises-doc
142     def __init__(
143         self,
144         circuit: cirq.AbstractCircuit,
145         measurement: BitstringsMeasurement,
146         params: Union[Sequence[TParamPair], cirq.ParamResolverOrSimilarType] = None,
147         spec: Optional[ExecutableSpec] = None,
148         problem_topology: Optional[cirq.NamedTopology] = None,
149         initial_state: Optional[cirq.ProductState] = None,
150     ):
151         """Initialize the quantum executable.
152 
153         The actual fields in this class are immutable, but we allow more liberal input types
154         which will be frozen in this __init__ method.
155 
156         Args:
157             circuit: The circuit. This will be frozen before being set as an attribute.
158             measurement: A description of the measurement properties or process.
159             params: A cirq.ParamResolverOrSimilarType which will be frozen into a tuple of
160                 key value pairs.
161             spec: Specification metadata about this executable that is not used by the quantum
162                 runtime, but is persisted in result objects to associate executables with results.
163             problem_topology: Description of the multiqubit gate topology present in the circuit.
164                 If not specified, the circuit must be compatible with the device topology.
165             initial_state: How to initialize the quantum system before running `circuit`. If not
166                 specified, the device will be initialized into the all-zeros state.
167         """
168 
169         # We care a lot about mutability in this class. No object is truly immutable in Python,
170         # but we can get pretty close by following the example of dataclass(frozen=True), which
171         # deletes this class's __setattr__ magic method. To set values ever, we use
172         # object.__setattr__ in this __init__ function.
173         #
174         # We write our own __init__ function to be able to accept a wider range of input formats
175         # that can be easily converted to our native, immutable format.
176         object.__setattr__(self, 'circuit', circuit.freeze())
177         object.__setattr__(self, 'measurement', measurement)
178 
179         if isinstance(params, tuple) and all(
180             isinstance(param_kv, tuple) and len(param_kv) == 2 for param_kv in params
181         ):
182             frozen_params = params
183         elif isinstance(params, Sequence) and all(
184             isinstance(param_kv, Sequence) and len(param_kv) == 2 for param_kv in params
185         ):
186             frozen_params = tuple((k, v) for k, v in params)
187         elif study.resolver._is_param_resolver_or_similar_type(params):
188             param_resolver = cirq.ParamResolver(cast(cirq.ParamResolverOrSimilarType, params))
189             frozen_params = tuple(param_resolver.param_dict.items())
190         else:
191             raise ValueError(f"`params` should be a ParamResolverOrSimilarType, not {params}.")
192         object.__setattr__(self, 'params', frozen_params)
193 
194         object.__setattr__(self, 'spec', spec)
195         object.__setattr__(self, 'problem_topology', problem_topology)
196         object.__setattr__(self, 'initial_state', initial_state)
197 
198         # Hash may be expensive to compute, especially for large circuits.
199         # This should be safe since this class should be immutable. This line will
200         # also check for hashibility of members at construction time.
201         object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))
202 
203     def __str__(self):
204         return f'QuantumExecutable(spec={self.spec})'
205 
206     def __repr__(self):
207         return _compat.dataclass_repr(self, namespace='cirq_google')
208 
209     @classmethod
210     def _json_namespace_(cls) -> str:
211         return 'cirq.google'
212 
213     def _json_dict_(self):
214         return cirq.dataclass_json_dict(self)
215 
216 
217 @dataclass(frozen=True)
218 class QuantumExecutableGroup:
219     """A collection of `QuantumExecutable`s.
220 
221     Attributes:
222         executables: A tuple of `cg.QuantumExecutable`.
223     """
224 
225     executables: Tuple[QuantumExecutable, ...]
226 
227     def __init__(
228         self,
229         executables: Sequence[QuantumExecutable],
230     ):
231         """Initialize and normalize the quantum executable group.
232 
233         Args:
234              executables: A sequence of `cg.QuantumExecutable` which will be frozen into a
235                 tuple.
236         """
237 
238         if not isinstance(executables, tuple):
239             executables = tuple(executables)
240         object.__setattr__(self, 'executables', executables)
241 
242         object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))
243 
244     def __len__(self) -> int:
245         return len(self.executables)
246 
247     def __iter__(self) -> Iterator[QuantumExecutable]:
248         yield from self.executables
249 
250     def __str__(self) -> str:
251         exe_str = ', '.join(str(exe) for exe in self.executables[:2])
252         if len(self.executables) > 2:
253             exe_str += ', ...'
254 
255         return f'QuantumExecutableGroup(executables=[{exe_str}])'
256 
257     def __repr__(self) -> str:
258         return _compat.dataclass_repr(self, namespace='cirq_google')
259 
260     def __hash__(self) -> int:
261         return self._hash  # type: ignore
262 
263     @classmethod
264     def _json_namespace_(cls) -> str:
265         return 'cirq.google'
266 
267     def _json_dict_(self) -> Dict[str, Any]:
268         return cirq.dataclass_json_dict(self)
269
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/cirq-google/cirq_google/workflow/quantum_executable.py b/cirq-google/cirq_google/workflow/quantum_executable.py
--- a/cirq-google/cirq_google/workflow/quantum_executable.py
+++ b/cirq-google/cirq_google/workflow/quantum_executable.py
@@ -53,6 +53,9 @@
     executable_family: str
     key_value_pairs: Tuple[Tuple[str, Any], ...] = ()
 
+    def to_dict(self) -> Dict[str, Any]:
+        return dict(self.key_value_pairs)
+
     @classmethod
     def _json_namespace_(cls) -> str:
         return 'cirq.google'

{"golden_diff": "diff --git a/cirq-google/cirq_google/workflow/quantum_executable.py b/cirq-google/cirq_google/workflow/quantum_executable.py\n--- a/cirq-google/cirq_google/workflow/quantum_executable.py\n+++ b/cirq-google/cirq_google/workflow/quantum_executable.py\n@@ -53,6 +53,9 @@\n     executable_family: str\n     key_value_pairs: Tuple[Tuple[str, Any], ...] = ()\n \n+    def to_dict(self) -> Dict[str, Any]:\n+        return dict(self.key_value_pairs)\n+\n     @classmethod\n     def _json_namespace_(cls) -> str:\n         return 'cirq.google'\n", "issue": "[cirqflow] `KeyValueExecutableSpec` should provide a `to_dict` method / override `__getitem__`\n**Is your feature request related to a use case or problem? Please describe.**\r\n`cg.KeyValueExecutableSpec` provides a nice `from_dict()` method to convert a dict into a `Tuple[Tuple[str, Any], ...]` which is hashable. This is useful when constructing the executable spec. However, using the executable spec during analysis of the results forces one to use the stored tuples, which is cumbersome.\r\n\r\n**Describe the solution you'd like**\r\nThe class should provide a similar `to_dict` method which can convert the stored `key_value_pairs` to a dictionary and return -- which are much easier to work with.  Though the method would be a simple `return dict(self.key_value_pairs)`, there might be some value in explicitly having it on the class.  We can also consider providing a custom `__getitem__` method. \r\n\r\n**What is the urgency from your perspective for this issue? Is it blocking important work?**\r\nP1 - I need this no later than the next release (end of quarter)\r\n\r\ncc @mpharrigan \n", "before_files": [{"content": "# Copyright 2021 The Cirq Developers\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Data structures for programs executable on a quantum runtime.\"\"\"\n\nimport abc\nimport dataclasses\nfrom dataclasses import dataclass\nfrom typing import Union, Tuple, Optional, Sequence, cast, Dict, Any, List, Iterator\n\nimport cirq\nfrom cirq import _compat, study\n\n\nclass ExecutableSpec(metaclass=abc.ABCMeta):\n    \"\"\"Specification metadata about an executable.\n\n    Subclasses should add problem-specific fields.\n    \"\"\"\n\n    executable_family: str = NotImplemented\n    \"\"\"A unique name to group executables.\"\"\"\n\n\n@dataclass(frozen=True)\nclass KeyValueExecutableSpec(ExecutableSpec):\n    \"\"\"A generic executable spec whose metadata is a list of key-value pairs.\n\n    The key-value pairs define an implicit data schema. Consider defining a problem-specific\n    subclass of `ExecutableSpec` instead of using this class to realize the benefits of having\n    an explicit schema.\n\n    See Also:\n        `KeyValueExecutableSpec.from_dict` will use a dictionary to populate `key_value_pairs`.\n\n    Args:\n        executable_family: A unique name to group executables.\n        key_value_pairs: A tuple of key-value pairs. The keys should be strings but the values\n            can be any immutable object.\n    \"\"\"\n\n    executable_family: str\n    key_value_pairs: Tuple[Tuple[str, Any], ...] = ()\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return cirq.dataclass_json_dict(self)\n\n    @classmethod\n    def from_dict(cls, d: Dict[str, Any], *, executable_family: str) -> 'KeyValueExecutableSpec':\n        return cls(\n            executable_family=executable_family,\n            key_value_pairs=tuple((k, v) for k, v in d.items()),\n        )\n\n    @classmethod\n    def _from_json_dict_(\n        cls, executable_family: str, key_value_pairs: List[List[Union[str, Any]]], **kwargs\n    ) -> 'KeyValueExecutableSpec':\n        return cls(\n            executable_family=executable_family,\n            key_value_pairs=tuple((k, v) for k, v in key_value_pairs),\n        )\n\n    def __repr__(self) -> str:\n        return cirq._compat.dataclass_repr(self, namespace='cirq_google')\n\n\n@dataclass(frozen=True)\nclass BitstringsMeasurement:\n    \"\"\"Use in-circuit MeasurementGate to collect many repetitions of strings of bits.\n\n    This is the lowest-level measurement type allowed in `QuantumExecutable` and behaves\n    identically to the `cirq.Sampler.run` function. The executable's circuit must contain\n    explicit measurement gates.\n\n    Args:\n        n_repeitions: The number of repetitions to execute the circuit.\n    \"\"\"\n\n    n_repetitions: int\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self):\n        return cirq.dataclass_json_dict(self)\n\n    def __repr__(self):\n        return cirq._compat.dataclass_repr(self, namespace='cirq_google')\n\n\nTParamPair = Tuple[cirq.TParamKey, cirq.TParamVal]\n\n\n@dataclass(frozen=True)\nclass QuantumExecutable:\n    \"\"\"An executable quantum program.\n\n    This serves a similar purpose to `cirq.Circuit` with some key differences. First, a quantum\n    executable contains all the relevant context for execution including parameters as well as\n    the desired number of repetitions. Second, this object is immutable. Finally, there are\n    optional fields enabling a higher level of abstraction for certain aspects of the executable.\n\n    Attributes:\n        circuit: A `cirq.Circuit` describing the quantum operations to execute.\n        measurement: A description of the measurement properties or process.\n        params: An immutable `cirq.ParamResolver` (or similar type). It's representation is\n            normalized to a tuple of key value pairs.\n        spec: Optional `cg.ExecutableSpec` containing metadata about this executable that is not\n            used by the quantum runtime, but will be forwarded to all downstream result objects.\n        problem_topology: Optional `cirq.NamedTopology` instance specifying the topology of the\n            circuit. This is useful when optimizing on-device layout. If none is provided we\n            assume `circuit` already has a valid on-device layout.\n        initial_state: A `cirq.ProductState` specifying the desired initial state before executing\n            `circuit`. If not specified, default to the all-zeros state.\n    \"\"\"\n\n    circuit: cirq.FrozenCircuit\n    measurement: BitstringsMeasurement\n    params: Optional[Tuple[TParamPair, ...]] = None\n    spec: Optional[ExecutableSpec] = None\n    problem_topology: Optional[cirq.NamedTopology] = None\n    initial_state: Optional[cirq.ProductState] = None\n\n    # pylint: disable=missing-raises-doc\n    def __init__(\n        self,\n        circuit: cirq.AbstractCircuit,\n        measurement: BitstringsMeasurement,\n        params: Union[Sequence[TParamPair], cirq.ParamResolverOrSimilarType] = None,\n        spec: Optional[ExecutableSpec] = None,\n        problem_topology: Optional[cirq.NamedTopology] = None,\n        initial_state: Optional[cirq.ProductState] = None,\n    ):\n        \"\"\"Initialize the quantum executable.\n\n        The actual fields in this class are immutable, but we allow more liberal input types\n        which will be frozen in this __init__ method.\n\n        Args:\n            circuit: The circuit. This will be frozen before being set as an attribute.\n            measurement: A description of the measurement properties or process.\n            params: A cirq.ParamResolverOrSimilarType which will be frozen into a tuple of\n                key value pairs.\n            spec: Specification metadata about this executable that is not used by the quantum\n                runtime, but is persisted in result objects to associate executables with results.\n            problem_topology: Description of the multiqubit gate topology present in the circuit.\n                If not specified, the circuit must be compatible with the device topology.\n            initial_state: How to initialize the quantum system before running `circuit`. If not\n                specified, the device will be initialized into the all-zeros state.\n        \"\"\"\n\n        # We care a lot about mutability in this class. No object is truly immutable in Python,\n        # but we can get pretty close by following the example of dataclass(frozen=True), which\n        # deletes this class's __setattr__ magic method. To set values ever, we use\n        # object.__setattr__ in this __init__ function.\n        #\n        # We write our own __init__ function to be able to accept a wider range of input formats\n        # that can be easily converted to our native, immutable format.\n        object.__setattr__(self, 'circuit', circuit.freeze())\n        object.__setattr__(self, 'measurement', measurement)\n\n        if isinstance(params, tuple) and all(\n            isinstance(param_kv, tuple) and len(param_kv) == 2 for param_kv in params\n        ):\n            frozen_params = params\n        elif isinstance(params, Sequence) and all(\n            isinstance(param_kv, Sequence) and len(param_kv) == 2 for param_kv in params\n        ):\n            frozen_params = tuple((k, v) for k, v in params)\n        elif study.resolver._is_param_resolver_or_similar_type(params):\n            param_resolver = cirq.ParamResolver(cast(cirq.ParamResolverOrSimilarType, params))\n            frozen_params = tuple(param_resolver.param_dict.items())\n        else:\n            raise ValueError(f\"`params` should be a ParamResolverOrSimilarType, not {params}.\")\n        object.__setattr__(self, 'params', frozen_params)\n\n        object.__setattr__(self, 'spec', spec)\n        object.__setattr__(self, 'problem_topology', problem_topology)\n        object.__setattr__(self, 'initial_state', initial_state)\n\n        # Hash may be expensive to compute, especially for large circuits.\n        # This should be safe since this class should be immutable. This line will\n        # also check for hashibility of members at construction time.\n        object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))\n\n    def __str__(self):\n        return f'QuantumExecutable(spec={self.spec})'\n\n    def __repr__(self):\n        return _compat.dataclass_repr(self, namespace='cirq_google')\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self):\n        return cirq.dataclass_json_dict(self)\n\n\n@dataclass(frozen=True)\nclass QuantumExecutableGroup:\n    \"\"\"A collection of `QuantumExecutable`s.\n\n    Attributes:\n        executables: A tuple of `cg.QuantumExecutable`.\n    \"\"\"\n\n    executables: Tuple[QuantumExecutable, ...]\n\n    def __init__(\n        self,\n        executables: Sequence[QuantumExecutable],\n    ):\n        \"\"\"Initialize and normalize the quantum executable group.\n\n        Args:\n             executables: A sequence of `cg.QuantumExecutable` which will be frozen into a\n                tuple.\n        \"\"\"\n\n        if not isinstance(executables, tuple):\n            executables = tuple(executables)\n        object.__setattr__(self, 'executables', executables)\n\n        object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))\n\n    def __len__(self) -> int:\n        return len(self.executables)\n\n    def __iter__(self) -> Iterator[QuantumExecutable]:\n        yield from self.executables\n\n    def __str__(self) -> str:\n        exe_str = ', '.join(str(exe) for exe in self.executables[:2])\n        if len(self.executables) > 2:\n            exe_str += ', ...'\n\n        return f'QuantumExecutableGroup(executables=[{exe_str}])'\n\n    def __repr__(self) -> str:\n        return _compat.dataclass_repr(self, namespace='cirq_google')\n\n    def __hash__(self) -> int:\n        return self._hash  # type: ignore\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return cirq.dataclass_json_dict(self)\n", "path": "cirq-google/cirq_google/workflow/quantum_executable.py"}], "after_files": [{"content": "# Copyright 2021 The Cirq Developers\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Data structures for programs executable on a quantum runtime.\"\"\"\n\nimport abc\nimport dataclasses\nfrom dataclasses import dataclass\nfrom typing import Union, Tuple, Optional, Sequence, cast, Dict, Any, List, Iterator\n\nimport cirq\nfrom cirq import _compat, study\n\n\nclass ExecutableSpec(metaclass=abc.ABCMeta):\n    \"\"\"Specification metadata about an executable.\n\n    Subclasses should add problem-specific fields.\n    \"\"\"\n\n    executable_family: str = NotImplemented\n    \"\"\"A unique name to group executables.\"\"\"\n\n\n@dataclass(frozen=True)\nclass KeyValueExecutableSpec(ExecutableSpec):\n    \"\"\"A generic executable spec whose metadata is a list of key-value pairs.\n\n    The key-value pairs define an implicit data schema. Consider defining a problem-specific\n    subclass of `ExecutableSpec` instead of using this class to realize the benefits of having\n    an explicit schema.\n\n    See Also:\n        `KeyValueExecutableSpec.from_dict` will use a dictionary to populate `key_value_pairs`.\n\n    Args:\n        executable_family: A unique name to group executables.\n        key_value_pairs: A tuple of key-value pairs. The keys should be strings but the values\n            can be any immutable object.\n    \"\"\"\n\n    executable_family: str\n    key_value_pairs: Tuple[Tuple[str, Any], ...] = ()\n\n    def to_dict(self) -> Dict[str, Any]:\n        return dict(self.key_value_pairs)\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return cirq.dataclass_json_dict(self)\n\n    @classmethod\n    def from_dict(cls, d: Dict[str, Any], *, executable_family: str) -> 'KeyValueExecutableSpec':\n        return cls(\n            executable_family=executable_family,\n            key_value_pairs=tuple((k, v) for k, v in d.items()),\n        )\n\n    @classmethod\n    def _from_json_dict_(\n        cls, executable_family: str, key_value_pairs: List[List[Union[str, Any]]], **kwargs\n    ) -> 'KeyValueExecutableSpec':\n        return cls(\n            executable_family=executable_family,\n            key_value_pairs=tuple((k, v) for k, v in key_value_pairs),\n        )\n\n    def __repr__(self) -> str:\n        return cirq._compat.dataclass_repr(self, namespace='cirq_google')\n\n\n@dataclass(frozen=True)\nclass BitstringsMeasurement:\n    \"\"\"Use in-circuit MeasurementGate to collect many repetitions of strings of bits.\n\n    This is the lowest-level measurement type allowed in `QuantumExecutable` and behaves\n    identically to the `cirq.Sampler.run` function. The executable's circuit must contain\n    explicit measurement gates.\n\n    Args:\n        n_repeitions: The number of repetitions to execute the circuit.\n    \"\"\"\n\n    n_repetitions: int\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self):\n        return cirq.dataclass_json_dict(self)\n\n    def __repr__(self):\n        return cirq._compat.dataclass_repr(self, namespace='cirq_google')\n\n\nTParamPair = Tuple[cirq.TParamKey, cirq.TParamVal]\n\n\n@dataclass(frozen=True)\nclass QuantumExecutable:\n    \"\"\"An executable quantum program.\n\n    This serves a similar purpose to `cirq.Circuit` with some key differences. First, a quantum\n    executable contains all the relevant context for execution including parameters as well as\n    the desired number of repetitions. Second, this object is immutable. Finally, there are\n    optional fields enabling a higher level of abstraction for certain aspects of the executable.\n\n    Attributes:\n        circuit: A `cirq.Circuit` describing the quantum operations to execute.\n        measurement: A description of the measurement properties or process.\n        params: An immutable `cirq.ParamResolver` (or similar type). It's representation is\n            normalized to a tuple of key value pairs.\n        spec: Optional `cg.ExecutableSpec` containing metadata about this executable that is not\n            used by the quantum runtime, but will be forwarded to all downstream result objects.\n        problem_topology: Optional `cirq.NamedTopology` instance specifying the topology of the\n            circuit. This is useful when optimizing on-device layout. If none is provided we\n            assume `circuit` already has a valid on-device layout.\n        initial_state: A `cirq.ProductState` specifying the desired initial state before executing\n            `circuit`. If not specified, default to the all-zeros state.\n    \"\"\"\n\n    circuit: cirq.FrozenCircuit\n    measurement: BitstringsMeasurement\n    params: Optional[Tuple[TParamPair, ...]] = None\n    spec: Optional[ExecutableSpec] = None\n    problem_topology: Optional[cirq.NamedTopology] = None\n    initial_state: Optional[cirq.ProductState] = None\n\n    # pylint: disable=missing-raises-doc\n    def __init__(\n        self,\n        circuit: cirq.AbstractCircuit,\n        measurement: BitstringsMeasurement,\n        params: Union[Sequence[TParamPair], cirq.ParamResolverOrSimilarType] = None,\n        spec: Optional[ExecutableSpec] = None,\n        problem_topology: Optional[cirq.NamedTopology] = None,\n        initial_state: Optional[cirq.ProductState] = None,\n    ):\n        \"\"\"Initialize the quantum executable.\n\n        The actual fields in this class are immutable, but we allow more liberal input types\n        which will be frozen in this __init__ method.\n\n        Args:\n            circuit: The circuit. This will be frozen before being set as an attribute.\n            measurement: A description of the measurement properties or process.\n            params: A cirq.ParamResolverOrSimilarType which will be frozen into a tuple of\n                key value pairs.\n            spec: Specification metadata about this executable that is not used by the quantum\n                runtime, but is persisted in result objects to associate executables with results.\n            problem_topology: Description of the multiqubit gate topology present in the circuit.\n                If not specified, the circuit must be compatible with the device topology.\n            initial_state: How to initialize the quantum system before running `circuit`. If not\n                specified, the device will be initialized into the all-zeros state.\n        \"\"\"\n\n        # We care a lot about mutability in this class. No object is truly immutable in Python,\n        # but we can get pretty close by following the example of dataclass(frozen=True), which\n        # deletes this class's __setattr__ magic method. To set values ever, we use\n        # object.__setattr__ in this __init__ function.\n        #\n        # We write our own __init__ function to be able to accept a wider range of input formats\n        # that can be easily converted to our native, immutable format.\n        object.__setattr__(self, 'circuit', circuit.freeze())\n        object.__setattr__(self, 'measurement', measurement)\n\n        if isinstance(params, tuple) and all(\n            isinstance(param_kv, tuple) and len(param_kv) == 2 for param_kv in params\n        ):\n            frozen_params = params\n        elif isinstance(params, Sequence) and all(\n            isinstance(param_kv, Sequence) and len(param_kv) == 2 for param_kv in params\n        ):\n            frozen_params = tuple((k, v) for k, v in params)\n        elif study.resolver._is_param_resolver_or_similar_type(params):\n            param_resolver = cirq.ParamResolver(cast(cirq.ParamResolverOrSimilarType, params))\n            frozen_params = tuple(param_resolver.param_dict.items())\n        else:\n            raise ValueError(f\"`params` should be a ParamResolverOrSimilarType, not {params}.\")\n        object.__setattr__(self, 'params', frozen_params)\n\n        object.__setattr__(self, 'spec', spec)\n        object.__setattr__(self, 'problem_topology', problem_topology)\n        object.__setattr__(self, 'initial_state', initial_state)\n\n        # Hash may be expensive to compute, especially for large circuits.\n        # This should be safe since this class should be immutable. This line will\n        # also check for hashibility of members at construction time.\n        object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))\n\n    def __str__(self):\n        return f'QuantumExecutable(spec={self.spec})'\n\n    def __repr__(self):\n        return _compat.dataclass_repr(self, namespace='cirq_google')\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self):\n        return cirq.dataclass_json_dict(self)\n\n\n@dataclass(frozen=True)\nclass QuantumExecutableGroup:\n    \"\"\"A collection of `QuantumExecutable`s.\n\n    Attributes:\n        executables: A tuple of `cg.QuantumExecutable`.\n    \"\"\"\n\n    executables: Tuple[QuantumExecutable, ...]\n\n    def __init__(\n        self,\n        executables: Sequence[QuantumExecutable],\n    ):\n        \"\"\"Initialize and normalize the quantum executable group.\n\n        Args:\n             executables: A sequence of `cg.QuantumExecutable` which will be frozen into a\n                tuple.\n        \"\"\"\n\n        if not isinstance(executables, tuple):\n            executables = tuple(executables)\n        object.__setattr__(self, 'executables', executables)\n\n        object.__setattr__(self, '_hash', hash(dataclasses.astuple(self)))\n\n    def __len__(self) -> int:\n        return len(self.executables)\n\n    def __iter__(self) -> Iterator[QuantumExecutable]:\n        yield from self.executables\n\n    def __str__(self) -> str:\n        exe_str = ', '.join(str(exe) for exe in self.executables[:2])\n        if len(self.executables) > 2:\n            exe_str += ', ...'\n\n        return f'QuantumExecutableGroup(executables=[{exe_str}])'\n\n    def __repr__(self) -> str:\n        return _compat.dataclass_repr(self, namespace='cirq_google')\n\n    def __hash__(self) -> int:\n        return self._hash  # type: ignore\n\n    @classmethod\n    def _json_namespace_(cls) -> str:\n        return 'cirq.google'\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return cirq.dataclass_json_dict(self)\n", "path": "cirq-google/cirq_google/workflow/quantum_executable.py"}]}