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 10:02 +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 = 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) 

50 

51 

52def has_reg_output(op: ClOp) -> bool: 

53 return op in _reg_output_clops 

54 

55 

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 

110 

111 

112@dataclass 

113class ExpressionConverter: 

114 bit_indices: dict[Bit, int] 

115 reg_indices: dict[BitRegister, int] 

116 

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) 

131 

132 

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` 

137 

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 ) 

169 

170 

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

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

173 

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

175 whole registers with the bits in the correct order. 

176 

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 

198 

199 

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)