Coverage for /home/runner/work/tket/tket/pytket/pytket/circuit/clexpr.py: 83%
102 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-09 15:08 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-09 15:08 +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 dataclasses import dataclass
16from typing import Any
18from pytket.circuit import (
19 Bit,
20 BitRegister,
21 Circuit,
22 ClBitVar,
23 ClExpr,
24 ClExprOp,
25 ClOp,
26 ClRegVar,
27 OpType,
28 WiredClExpr,
29)
30from pytket.circuit.logic_exp import BitWiseOp, LogicExp, Ops, RegWiseOp
32_reg_output_clops = {
33 ClOp.RegAnd,
34 ClOp.RegOr,
35 ClOp.RegXor,
36 ClOp.RegNot,
37 ClOp.RegZero,
38 ClOp.RegOne,
39 ClOp.RegAdd,
40 ClOp.RegSub,
41 ClOp.RegMul,
42 ClOp.RegDiv,
43 ClOp.RegPow,
44 ClOp.RegLsh,
45 ClOp.RegRsh,
46 ClOp.RegNeg,
47}
50def _has_reg_output(op: ClOp) -> bool:
51 return op in _reg_output_clops
54def _clop_from_ops(op: Ops) -> ClOp: # noqa: PLR0911, PLR0912
55 match op:
56 case BitWiseOp.AND:
57 return ClOp.BitAnd
58 case BitWiseOp.OR:
59 return ClOp.BitOr
60 case BitWiseOp.XOR:
61 return ClOp.BitXor
62 case BitWiseOp.EQ: 62 ↛ 63line 62 didn't jump to line 63 because the pattern on line 62 never matched
63 return ClOp.BitEq
64 case BitWiseOp.NEQ: 64 ↛ 65line 64 didn't jump to line 65 because the pattern on line 64 never matched
65 return ClOp.BitNeq
66 case BitWiseOp.NOT:
67 return ClOp.BitNot
68 case BitWiseOp.ZERO: 68 ↛ 69line 68 didn't jump to line 69 because the pattern on line 68 never matched
69 return ClOp.BitZero
70 case BitWiseOp.ONE:
71 return ClOp.BitOne
72 case RegWiseOp.AND:
73 return ClOp.RegAnd
74 case RegWiseOp.OR:
75 return ClOp.RegOr
76 case RegWiseOp.XOR:
77 return ClOp.RegXor
78 case RegWiseOp.EQ: 78 ↛ 79line 78 didn't jump to line 79 because the pattern on line 78 never matched
79 return ClOp.RegEq
80 case RegWiseOp.NEQ: 80 ↛ 81line 80 didn't jump to line 81 because the pattern on line 80 never matched
81 return ClOp.RegNeq
82 case RegWiseOp.LT: 82 ↛ 83line 82 didn't jump to line 83 because the pattern on line 82 never matched
83 return ClOp.RegLt
84 case RegWiseOp.GT: 84 ↛ 85line 84 didn't jump to line 85 because the pattern on line 84 never matched
85 return ClOp.RegGt
86 case RegWiseOp.LEQ: 86 ↛ 87line 86 didn't jump to line 87 because the pattern on line 86 never matched
87 return ClOp.RegLeq
88 case RegWiseOp.GEQ: 88 ↛ 89line 88 didn't jump to line 89 because the pattern on line 88 never matched
89 return ClOp.RegGeq
90 case RegWiseOp.ADD:
91 return ClOp.RegAdd
92 case RegWiseOp.SUB:
93 return ClOp.RegSub
94 case RegWiseOp.MUL:
95 return ClOp.RegMul
96 case RegWiseOp.DIV:
97 return ClOp.RegDiv
98 case RegWiseOp.POW:
99 return ClOp.RegPow
100 case RegWiseOp.LSH:
101 return ClOp.RegLsh
102 case RegWiseOp.RSH: 102 ↛ 104line 102 didn't jump to line 104 because the pattern on line 102 always matched
103 return ClOp.RegRsh
104 case RegWiseOp.NOT:
105 return ClOp.RegNot
106 case RegWiseOp.NEG:
107 return ClOp.RegNeg
110@dataclass
111class _ExpressionConverter:
112 bit_indices: dict[Bit, int]
113 reg_indices: dict[BitRegister, int]
115 def convert(self, exp: LogicExp) -> ClExpr:
116 op: ClOp = _clop_from_ops(exp.op)
117 args: list[int | ClBitVar | ClRegVar | ClExpr] = []
118 for arg in exp.args:
119 if isinstance(arg, LogicExp):
120 args.append(self.convert(arg))
121 elif isinstance(arg, Bit):
122 args.append(ClBitVar(self.bit_indices[arg]))
123 elif isinstance(arg, BitRegister):
124 args.append(ClRegVar(self.reg_indices[arg]))
125 else:
126 assert isinstance(arg, int)
127 args.append(arg)
128 return ClExpr(op, args)
131def wired_clexpr_from_logic_exp(
132 exp: LogicExp, output_bits: list[Bit]
133) -> tuple[WiredClExpr, list[Bit]]:
134 """Convert a :py:class:`LogicExp` to a :py:class:`WiredClExpr`
136 :param exp: the LogicExp
137 :param output_bits: list of output bits of the LogicExp
138 :return: the WiredClExpr and its full list of arguments
139 """
140 # 1. Construct lists of input bits and registers (where the positions of the items
141 # in each list will be the indices of the corresponding variables in the ClExpr):
142 all_vars = exp.all_inputs_ordered()
143 input_bits: list[Bit] = [var for var in all_vars if isinstance(var, Bit)]
144 input_regs: list[BitRegister] = [
145 var for var in all_vars if isinstance(var, BitRegister)
146 ]
147 # 2. Order the arguments: first the input bits, then all the bits in the input
148 # registers then any remaining output bits:
149 args = []
150 args.extend(input_bits)
151 for r in input_regs:
152 args.extend(r.to_list())
153 args.extend(b for b in output_bits if b not in args)
154 # 3. Construct the WiredClExpr and return it with the argument list:
155 return (
156 WiredClExpr(
157 _ExpressionConverter(
158 {b: i for i, b in enumerate(input_bits)},
159 {r: i for i, r in enumerate(input_regs)},
160 ).convert(exp),
161 {i: args.index(b) for i, b in enumerate(input_bits)},
162 {i: [args.index(b) for b in r.to_list()] for i, r in enumerate(input_regs)},
163 [args.index(b) for b in output_bits],
164 ),
165 args,
166 )
169def check_register_alignments(circ: Circuit) -> bool:
170 """Check whether all `ClExprOp` operations in the circuit are register-aligned.
172 This means that all register variables and outputs occurring in `ClExprOp` comprise
173 whole registers with the bits in the correct order.
175 :param circ: circuit to check
176 :return: True iff all `ClExprOp` operations are register-aligned
177 """
178 cregs: set[tuple[Bit, ...]] = {tuple(creg.to_list()) for creg in circ.c_registers}
179 for cmd in circ:
180 op = cmd.op
181 if op.type == OpType.ClExpr:
182 assert isinstance(op, ClExprOp)
183 wexpr: WiredClExpr = op.expr
184 args = cmd.args
185 if any( 185 ↛ 192line 185 didn't jump to line 192 because the condition on line 185 was never true
186 tuple(args[i] for i in poslist) not in cregs
187 for poslist in wexpr.reg_posn.values()
188 ) or (
189 _has_reg_output(wexpr.expr.op)
190 and tuple(args[i] for i in wexpr.output_posn) not in cregs
191 ):
192 return False
193 return True
196def _add_clexpr_to_circuit_from_logicexp(
197 circ: Circuit, exp: LogicExp, output_bits: list[Bit], **kwargs: Any
198) -> None:
199 wexpr, args = wired_clexpr_from_logic_exp(exp, output_bits)
200 circ.add_clexpr(wexpr, args, **kwargs)