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