Coverage for /home/runner/work/tket/tket/pytket/pytket/utils/serialization/migration.py: 90%
100 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 10:53 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 10:53 +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.
15from copy import deepcopy
16from typing import Any
18from pytket.circuit.logic_exp import ( # noqa: F401 pylint: disable=W0611
19 BitWiseOp,
20 LogicExp,
21 RegWiseOp,
22)
25def _convert_op(exp_op: str) -> str:
26 assert exp_op.startswith(("RegWiseOp.", "BitWiseOp."))
27 return LogicExp.factory(eval(exp_op)).__name__
30def _convert_regwise_terms(args: list, regs: list[tuple[str, int]]) -> list:
31 terms = []
32 for arg in args:
33 assert isinstance(arg, dict)
34 regname = arg.get("name")
35 if regname is None:
36 # It's a nested expression.
37 terms.append(
38 {
39 "type": "expr",
40 "input": {
41 "op": _convert_op(arg["op"]),
42 "args": _convert_regwise_terms(arg["args"], regs),
43 },
44 }
45 )
46 else:
47 # It's a register.
48 i = regs.index((regname, arg["size"]))
49 terms.append(
50 {
51 "type": "term",
52 "input": {
53 "type": "var",
54 "term": {"type": "reg", "var": {"index": i}},
55 },
56 }
57 )
58 return terms
61def _convert_bitwise_terms(args: list, bits: list[tuple[str, tuple[int]]]) -> list:
62 terms = []
63 for arg in args:
64 if isinstance(arg, dict): 64 ↛ 66line 64 didn't jump to line 66 because the condition on line 64 was never true
65 # It's a nested expression.
66 terms.append(
67 {
68 "type": "expr",
69 "input": {
70 "op": _convert_op(arg["op"]),
71 "args": _convert_bitwise_terms(arg["args"], bits),
72 },
73 }
74 )
75 else:
76 # It's a bit.
77 assert isinstance(arg, list)
78 assert len(arg) == 2
79 name, index = arg
80 i = bits.index((name, tuple(index)))
81 terms.append(
82 {
83 "type": "term",
84 "input": {
85 "type": "var",
86 "term": {"type": "bit", "var": {"index": i}},
87 },
88 }
89 )
90 return terms
93def _find_regs_in_expr(exp: dict[str, Any], regs: list[tuple[str, int]]) -> None:
94 op = exp["op"]
95 args = exp["args"]
96 assert op.startswith("RegWiseOp.")
97 for arg in args:
98 assert isinstance(arg, dict)
99 regname = arg.get("name")
100 if regname is None:
101 # It's a nested expression.
102 _find_regs_in_expr(arg, regs)
103 else:
104 # It's a register.
105 reg = (regname, arg["size"])
106 if reg not in regs:
107 regs.append(reg)
110def _find_bits_in_expr(exp: dict[str, Any], bits: list[tuple[str, tuple[int]]]) -> None:
111 op = exp["op"]
112 args = exp["args"]
113 assert op.startswith("BitWiseOp.")
114 for arg in args:
115 if isinstance(arg, dict): 115 ↛ 117line 115 didn't jump to line 117 because the condition on line 115 was never true
116 # It's a nested expression.
117 _find_bits_in_expr(arg, bits)
118 else:
119 # It's a bit.
120 assert isinstance(arg, list)
121 bit = (arg[0], tuple(arg[1]))
122 if bit not in bits: 122 ↛ 114line 122 didn't jump to line 114 because the condition on line 122 was always true
123 bits.append(bit)
126def _clexpr_from_regwise_classicalexpbox(
127 box: dict[str, Any], command_args: list
128) -> dict[str, Any]:
129 exp = box["exp"]
130 regs: list[tuple[str, int]] = []
131 _find_regs_in_expr(exp, regs) # construct ordered list of reg vars
132 reg_posn = [
133 [i, [command_args.index([name, [j]]) for j in range(size)]]
134 for i, (name, size) in enumerate(regs)
135 ]
136 terms = _convert_regwise_terms(exp["args"], regs)
137 return {
138 "expr": {"op": _convert_op(exp["op"]), "args": terms},
139 "bit_posn": [],
140 "reg_posn": reg_posn,
141 "output_posn": list(range(box["n_i"], len(command_args))),
142 }
145def _clexpr_from_bitwise_classicalexpbox(
146 box: dict[str, Any], command_args: list
147) -> dict[str, Any]:
148 exp = box["exp"]
149 bits: list[tuple[str, tuple[int]]] = []
150 _find_bits_in_expr(exp, bits) # construct ordered list of bit vars
151 bit_posn = [
152 [i, command_args.index([name, list(index)])]
153 for i, (name, index) in enumerate(bits)
154 ]
155 terms = _convert_bitwise_terms(exp["args"], bits)
156 return {
157 "expr": {"op": _convert_op(exp["op"]), "args": terms},
158 "bit_posn": bit_posn,
159 "reg_posn": [],
160 "output_posn": list(range(box["n_i"], len(command_args))),
161 }
164def _clexpr_from_classicalexpbox(
165 box: dict[str, Any], command_args: list
166) -> dict[str, Any]:
167 exp_op = box["exp"]["op"]
168 if exp_op.startswith("RegWiseOp."):
169 return _clexpr_from_regwise_classicalexpbox(box, command_args)
170 assert exp_op.startswith("BitWiseOp.")
171 return _clexpr_from_bitwise_classicalexpbox(box, command_args)
174def _new_op(op: dict[str, Any], args: list) -> dict[str, Any]:
175 match op["type"]:
176 case "Conditional":
177 new_data = deepcopy(op)
178 new_data["conditional"]["op"] = _new_op(
179 op["conditional"]["op"], args[op["conditional"]["width"] :]
180 )
181 return new_data
182 case "QControlBox": 182 ↛ 183line 182 didn't jump to line 183 because the pattern on line 182 never matched
183 new_data = deepcopy(op)
184 new_data["box"]["op"] = _new_op(op["box"]["op"], args)
185 return new_data
186 case "CircBox":
187 new_data = deepcopy(op)
188 new_data["box"]["circuit"] = circuit_dict_from_pytket1_dict(
189 op["box"]["circuit"]
190 )
191 return new_data
192 case "CustomGate": 192 ↛ 193line 192 didn't jump to line 193 because the pattern on line 192 never matched
193 new_data = deepcopy(op)
194 new_data["box"]["gate"] = circuit_dict_from_pytket1_dict(op["box"]["gate"])
195 return new_data
196 case "ClassicalExpBox":
197 return {
198 "type": "ClExpr",
199 "expr": _clexpr_from_classicalexpbox(op["box"], args),
200 }
201 case _:
202 return op
205def _new_command(command: dict[str, Any]) -> dict[str, Any]:
206 new_data = deepcopy(command)
207 new_data["op"] = _new_op(command["op"], command["args"])
208 return new_data
211def circuit_dict_from_pytket1_dict(circuit_data: dict[str, Any]) -> dict[str, Any]:
212 """Update the serialization of a pytket 1 circuit to an equivalent pytket circuit.
213 (This converts ClassicalExpBox to ClExprOp operations.)
215 :param circuit_data: serialization of pytket 1 circuit
216 :return: serialization of equivalent pytket circuit
217 """
218 # Replace all ClassicalExpBox ops. Need to recurse into CircBoxes etc.
219 new_data = deepcopy(circuit_data)
220 new_data["commands"] = [
221 _new_command(command) for command in circuit_data["commands"]
222 ]
223 return new_data