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

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. 

14 

15from dataclasses import dataclass 

16from typing import Any 

17 

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 

31 

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} 

48 

49 

50def _has_reg_output(op: ClOp) -> bool: 

51 return op in _reg_output_clops 

52 

53 

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 

108 

109 

110@dataclass 

111class _ExpressionConverter: 

112 bit_indices: dict[Bit, int] 

113 reg_indices: dict[BitRegister, int] 

114 

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) 

129 

130 

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` 

135 

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 ) 

167 

168 

169def check_register_alignments(circ: Circuit) -> bool: 

170 """Check whether all `ClExprOp` operations in the circuit are register-aligned. 

171 

172 This means that all register variables and outputs occurring in `ClExprOp` comprise 

173 whole registers with the bits in the correct order. 

174 

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 

194 

195 

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)