Coverage for /home/runner/work/tket/tket/pytket/pytket/backends/backendinfo.py: 99%
116 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-14 11:30 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-14 11:30 +0000
1# Copyright Quantinuum
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# http://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.
15"""BackendInfo class: additional information on Backends"""
17from dataclasses import asdict, dataclass, field
18from typing import Any
20from pytket.architecture import Architecture, FullyConnected
21from pytket.circuit import Node, OpType
23_OpTypeErrs = dict[OpType, float]
24_Edge = tuple[Node, Node]
27def _serialize_all_node_gate_errors(
28 d: dict[Node, _OpTypeErrs] | None,
29) -> list[list] | None:
30 if d is None:
31 return None
32 return [
33 [n.to_list(), {ot.name: err for ot, err in errs.items()}]
34 for n, errs in d.items()
35 ]
38def _deserialize_all_node_gate_errors(
39 l: list[list] | None,
40) -> dict[Node, _OpTypeErrs] | None:
41 if l is None:
42 return None
43 return {
44 Node.from_list(n): {OpType.from_name(ot): err for ot, err in errs.items()}
45 for n, errs in l
46 }
49def _serialize_all_edge_gate_errors(d: dict[_Edge, _OpTypeErrs] | None) -> list | None:
50 if d is None:
51 return None
52 return [
53 [[n0.to_list(), n1.to_list()], {ot.name: err for ot, err in errs.items()}]
54 for (n0, n1), errs in d.items()
55 ]
58def _deserialize_all_edge_gate_errors(
59 l: list | None,
60) -> dict[_Edge, _OpTypeErrs] | None:
61 if l is None:
62 return None
63 return {
64 (Node.from_list(n0), Node.from_list(n1)): {
65 OpType.from_name(ot): err for ot, err in errs.items()
66 }
67 for (n0, n1), errs in l
68 }
71def _serialize_all_readout_errors(
72 d: dict[Node, list[list[float]]] | None,
73) -> list[list] | None:
74 if d is None:
75 return None
76 return [[n.to_list(), errs] for n, errs in d.items()]
79def _deserialize_all_readout_errors(
80 l: list[list] | None,
81) -> dict[Node, list[list[float]]] | None:
82 if l is None:
83 return None
84 return {Node.from_list(n): errs for n, errs in l}
87def _serialize_averaged_node_gate_errors(
88 d: dict[Node, float] | None,
89) -> list[list] | None:
90 if d is None:
91 return None
92 return [[n.to_list(), err] for n, err in d.items()]
95def _deserialize_averaged_node_gate_errors(
96 l: list[list] | None,
97) -> dict[Node, float] | None:
98 if l is None:
99 return None
100 return {Node.from_list(n): err for n, err in l}
103def _serialize_averaged_edge_gate_errors(
104 d: dict[_Edge, float] | None,
105) -> list[list] | None:
106 if d is None:
107 return None
108 return [[[n0.to_list(), n1.to_list()], err] for (n0, n1), err in d.items()]
111def _deserialize_averaged_edge_gate_errors(
112 l: list[list] | None,
113) -> dict[tuple, float] | None:
114 if l is None:
115 return None
116 return {(Node.from_list(n0), Node.from_list(n1)): err for (n0, n1), err in l}
119def _serialize_averaged_readout_errors(
120 d: dict[Node, float] | None,
121) -> list[list] | None:
122 if d is None:
123 return None
124 return [[n.to_list(), err] for n, err in d.items()]
127def _deserialize_averaged_readout_errors(
128 l: list[list] | None,
129) -> dict[Node, float] | None:
130 if l is None:
131 return None
132 return {Node.from_list(n): err for n, err in l}
135@dataclass
136class BackendInfo:
137 """
138 Stores various properties of a Backend.
140 This provides all device information useful for compilation.
142 :param name: Class name of the backend.
143 :param device_name: Name of the device.
144 :param version: Pytket-extension version installed when creating object.
145 :param architecture: Optional device connectivity.
146 :param gate_set: Set of supported gate types.
147 :param n_cl_reg: number of classical registers supported.
148 :param supports_fast_feedforward: Flag for hardware support of fast feedforward.
149 :param supports_reset: Flag for hardware support of reset operation
150 :param supports_midcircuit_meas: Flag for hardware support of midcircuit
151 measurement.
152 :param all_node_gate_errors: Dictionary between architecture Node and error rate
153 for different single qubit operations.
154 :param all_edge_gate_errors: Dictionary between architecture couplings and error
155 rate for different two-qubit operations.
156 :param all_readout_errors: Dictionary between architecture Node and uncorrelated
157 single qubit readout errors (2x2 readout probability matrix).
158 :param averaged_node_gate_errors: Dictionary between architecture Node and averaged
159 error rate for all single qubit operations.
160 :param averaged_edge_gate_errors: Dictionary between architecture couplings and
161 averaged error rate for all two-qubit operations.
162 :param averaged_readout_errors: Dictionary between architecture Node and averaged
163 readout errors.
164 :param misc: key-value map with further provider-specific information (must be
165 JSON-serializable)
166 """
168 # identifying information
169 name: str
170 device_name: str | None
171 version: str
172 # hardware constraints
173 architecture: Architecture | FullyConnected | None
174 gate_set: set[OpType]
175 n_cl_reg: int | None = None
176 # additional feature support
177 supports_fast_feedforward: bool = False
178 supports_reset: bool = False
179 supports_midcircuit_measurement: bool = False
180 # additional basic device characterisation information
181 all_node_gate_errors: dict[Node, dict[OpType, float]] | None = None
182 all_edge_gate_errors: dict[tuple[Node, Node], dict[OpType, float]] | None = None
183 all_readout_errors: dict[Node, list[list[float]]] | None = None
184 averaged_node_gate_errors: dict[Node, float] | None = None
185 averaged_edge_gate_errors: dict[tuple[Node, Node], float] | None = None
186 averaged_readout_errors: dict[Node, float] | None = None
188 # miscellaneous, eg additional noise characterisation and provider-supplied
189 # information
190 misc: dict[str, Any] = field(default_factory=dict)
192 @property
193 def nodes(self) -> list[Node]:
194 """
195 List of device nodes of the backend. Returns empty list
196 if the `architecture` field is not provided.
198 :return: List of nodes.
199 :rtype: List[Node]
200 """
201 if self.architecture is None: 201 ↛ 202line 201 didn't jump to line 202 because the condition on line 201 was never true
202 return []
203 return self.architecture.nodes
205 @property
206 def n_nodes(self) -> int:
207 """
208 Number of nodes in the architecture of the device. Returns 0
209 if the `architecture` field is not provided.
211 :return: Number of nodes.
212 :rtype: int
213 """
214 return len(self.nodes)
216 def add_misc(self, key: str, val: Any) -> None:
217 """
218 Add a new entry in BackendInfo's dictionary of additional information.
220 :param key: Key to store and retrieve value.
221 :type key: str
222 :param val: Value to be stored.
223 """
224 if key in self.misc:
225 raise KeyError("Attempting to add an already existing entry to misc dict")
226 self.misc[key] = val
228 def get_misc(self, key: str) -> Any:
229 """
230 Retrieve information stored in Backend's additional information store
232 :param key: Key to retrieve value.
233 :type key: str
235 :raises KeyError: There is no value stored with the given key.
237 :return: The value stored at the given key.
238 """
239 return self.misc[key]
241 def to_dict(self) -> dict[str, Any]:
242 """
243 Generate a dictionary serialized representation of BackendInfo,
244 suitable for writing to JSON.
246 :return: JSON serializable dictionary.
247 :rtype: Dict[str, Any]
248 """
250 self_dict = asdict(self)
251 if self_dict["architecture"] is not None:
252 self_dict["architecture"] = self_dict["architecture"].to_dict()
253 self_dict["gate_set"] = [op.value for op in self_dict["gate_set"]]
254 self_dict["all_node_gate_errors"] = _serialize_all_node_gate_errors(
255 self_dict["all_node_gate_errors"]
256 )
257 self_dict["all_edge_gate_errors"] = _serialize_all_edge_gate_errors(
258 self_dict["all_edge_gate_errors"]
259 )
260 self_dict["all_readout_errors"] = _serialize_all_readout_errors(
261 self_dict["all_readout_errors"]
262 )
263 self_dict["averaged_node_gate_errors"] = _serialize_averaged_node_gate_errors(
264 self_dict["averaged_node_gate_errors"]
265 )
266 self_dict["averaged_edge_gate_errors"] = _serialize_averaged_edge_gate_errors(
267 self_dict["averaged_edge_gate_errors"]
268 )
269 self_dict["averaged_readout_errors"] = _serialize_averaged_readout_errors(
270 self_dict["averaged_readout_errors"]
271 )
272 return self_dict
274 @classmethod
275 def from_dict(cls, d: dict[str, Any]) -> "BackendInfo":
276 """
277 Construct BackendInfo object from JSON serializable dictionary
278 representation, as generated by BackendInfo.to_dict.
280 :return: Instance of BackendInfo constructed from dictionary.
281 :rtype: BackendInfo
282 """
283 args = dict(**d)
284 arch = args["architecture"]
285 if arch is not None:
286 if "links" in arch:
287 args["architecture"] = Architecture.from_dict(args["architecture"])
288 else:
289 args["architecture"] = FullyConnected.from_dict(args["architecture"])
290 args["gate_set"] = {OpType(op) for op in args["gate_set"]}
291 args["all_node_gate_errors"] = _deserialize_all_node_gate_errors(
292 args["all_node_gate_errors"]
293 )
294 args["all_edge_gate_errors"] = _deserialize_all_edge_gate_errors(
295 args["all_edge_gate_errors"]
296 )
297 args["all_readout_errors"] = _deserialize_all_readout_errors(
298 args["all_readout_errors"]
299 )
300 args["averaged_node_gate_errors"] = _deserialize_averaged_node_gate_errors(
301 args["averaged_node_gate_errors"]
302 )
303 args["averaged_edge_gate_errors"] = _deserialize_averaged_edge_gate_errors(
304 args["averaged_edge_gate_errors"]
305 )
306 args["averaged_readout_errors"] = _deserialize_averaged_readout_errors(
307 args["averaged_readout_errors"]
308 )
309 return cls(**args)
312def fully_connected_backendinfo( # type: ignore
313 name: str,
314 device_name: str | None,
315 version: str,
316 n_nodes: int,
317 gate_set: set[OpType],
318 **kwargs
319) -> BackendInfo:
320 """
321 Construct a BackendInfo with a FullyConnected architecture.
323 :param name: Class name of the backend.
324 :param device_name: Name of the device.
325 :param version: Version of the pytket Backend.
326 :param n_nodes: Number of nodes of the device.
327 :param gate_set: Set of supported gate types.
328 :param \\**kwargs: All further arguments are passed to the BackendInfo constructor.
329 """
330 return BackendInfo(
331 name, device_name, version, FullyConnected(n_nodes), gate_set, **kwargs
332 )