Coverage for /home/runner/work/tket/tket/pytket/pytket/circuit/logic_exp.py: 88%

555 statements  

« 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. 

14 

15"""Classes and functions for constructing logical 

16expressions over Bit and BitRegister.""" 

17from collections.abc import Iterable, Iterator, Sequence 

18from dataclasses import dataclass 

19from enum import Enum 

20from typing import Any, ClassVar, TypeVar, Union, cast 

21 

22from pytket.circuit import Bit, BitRegister 

23 

24T = TypeVar("T") 

25 

26 

27def filter_by_type(seq: Iterable, var_type: type[T]) -> Iterator[tuple[int, T]]: 

28 """Return enumeration of seq, with only elements of type var_type.""" 

29 return filter(lambda x: isinstance(x[1], var_type), enumerate(seq)) 

30 

31 

32class BitWiseOp(Enum): 

33 """Enum for operations on Bit.""" 

34 

35 AND = "&" 

36 OR = "|" 

37 XOR = "^" 

38 EQ = "==" 

39 NEQ = "!=" 

40 NOT = "~" 

41 ZERO = "0" 

42 ONE = "1" 

43 

44 

45class RegWiseOp(Enum): 

46 """Enum for operations on BitRegister.""" 

47 

48 AND = "&" 

49 OR = "|" 

50 XOR = "^" 

51 EQ = "==" 

52 NEQ = "!=" 

53 LT = "<" 

54 GT = ">" 

55 LEQ = "<=" 

56 GEQ = ">=" 

57 ADD = "+" 

58 SUB = "-" 

59 MUL = "*" 

60 DIV = "/" 

61 POW = "**" 

62 LSH = "<<" 

63 RSH = ">>" 

64 NOT = "~" 

65 NEG = "-" # noqa: PIE796 

66 

67 

68Ops = Union[BitWiseOp, RegWiseOp] # all op enum types 

69 

70 

71Constant = int # constants in expression 

72Variable = Union[Bit, BitRegister] # variables in expression 

73ArgType = Union["LogicExp", Variable, Constant] # all possible arguments in expression 

74 

75 

76@dataclass(init=False) 

77class LogicExp: 

78 """Logical expressions over Bit or BitRegister. 

79 Encoded as a tree of expressions""" 

80 

81 op: Ops # enum for operation encoded by this node 

82 args: list[ArgType] # arguments of operation 

83 # class level dictionary mapping enum to class 

84 op_cls_dict: ClassVar[dict[Ops, type["LogicExp"]]] = dict() 

85 

86 @classmethod 

87 def factory(cls, op: Ops) -> type["LogicExp"]: 

88 """Return matching operation class for enum.""" 

89 # RegNeg cannot be initialised this way as "-" clashes with SUB 

90 if op == BitWiseOp.AND: 

91 return BitAnd 

92 if op == BitWiseOp.OR: 

93 return BitOr 

94 if op == BitWiseOp.XOR: 

95 return BitXor 

96 if op == BitWiseOp.NOT: 

97 return BitNot 

98 if op == BitWiseOp.EQ: 

99 return BitEq 

100 if op == BitWiseOp.NEQ: 

101 return BitNeq 

102 if op == BitWiseOp.ZERO: 

103 return BitZero 

104 if op == BitWiseOp.ONE: 

105 return BitOne 

106 if op == RegWiseOp.AND: 

107 return RegAnd 

108 if op == RegWiseOp.OR: 

109 return RegOr 

110 if op == RegWiseOp.XOR: 110 ↛ 111line 110 didn't jump to line 111 because the condition on line 110 was never true

111 return RegXor 

112 if op == RegWiseOp.ADD: 

113 return RegAdd 

114 if op == RegWiseOp.SUB: 

115 return RegSub 

116 if op == RegWiseOp.MUL: 

117 return RegMul 

118 if op == RegWiseOp.DIV: 

119 return RegDiv 

120 if op == RegWiseOp.POW: 120 ↛ 121line 120 didn't jump to line 121 because the condition on line 120 was never true

121 return RegPow 

122 if op == RegWiseOp.LSH: 

123 return RegLsh 

124 if op == RegWiseOp.RSH: 124 ↛ 125line 124 didn't jump to line 125 because the condition on line 124 was never true

125 return RegRsh 

126 if op == RegWiseOp.EQ: 126 ↛ 127line 126 didn't jump to line 127 because the condition on line 126 was never true

127 return RegEq 

128 if op == RegWiseOp.NEQ: 

129 return RegNeq 

130 if op == RegWiseOp.LT: 130 ↛ 131line 130 didn't jump to line 131 because the condition on line 130 was never true

131 return RegLt 

132 if op == RegWiseOp.GT: 

133 return RegGt 

134 if op == RegWiseOp.LEQ: 

135 return RegLeq 

136 if op == RegWiseOp.GEQ: 136 ↛ 137line 136 didn't jump to line 137 because the condition on line 136 was never true

137 return RegGeq 

138 if op == RegWiseOp.NOT: 138 ↛ 140line 138 didn't jump to line 140 because the condition on line 138 was always true

139 return RegNot 

140 raise ValueError("op type not supported") 

141 

142 def set_value(self, var: Variable, val: Constant) -> None: 

143 """Set value of var to val recursively.""" 

144 for i, arg in enumerate(self.args): 

145 if isinstance(arg, (Bit, BitRegister)): 

146 if arg == var: 

147 self.args[i] = val 

148 elif isinstance(arg, LogicExp): 148 ↛ 149line 148 didn't jump to line 149 because the condition on line 148 was never true

149 arg.set_value(var, val) 

150 

151 @staticmethod 

152 def _const_eval(args: list[Constant]) -> Constant: 

153 """Evaluate expression given constant values for all args.""" 

154 raise NotImplementedError 

155 

156 def eval_vals(self) -> ArgType: 

157 """Attempt to evaluate all sub-expressions; simple constant folding.""" 

158 rval: ArgType = self 

159 for i, arg in filter_by_type(self.args, LogicExp): 159 ↛ 160line 159 didn't jump to line 160 because the loop on line 159 never started

160 self.args[i] = arg.eval_vals() 

161 if all(isinstance(a, Constant) for a in self.args): 161 ↛ 166line 161 didn't jump to line 166 because the condition on line 161 was always true

162 try: 

163 rval = self._const_eval(cast(list[Constant], self.args)) 

164 except NotImplementedError: 

165 pass 

166 return rval 

167 

168 def all_inputs(self) -> set[Variable]: 

169 """ 

170 :return: All variables involved in expression. 

171 :rtype: Set[Variable] 

172 """ 

173 outset: set[Variable] = set() 

174 

175 for arg in self.args: 

176 if isinstance(arg, LogicExp): 

177 outset.update(arg.all_inputs()) 

178 continue 

179 if isinstance(self, BitLogicExp): 

180 if isinstance(arg, Bit): 

181 outset.add(arg) 

182 elif isinstance(arg, BitRegister): 

183 outset.add(arg) 

184 return outset 

185 

186 def all_inputs_ordered(self) -> list[Variable]: 

187 """ 

188 :return: All variables involved in expression, in order of first appearance. 

189 :rtype: list[Variable] 

190 """ 

191 # use dict[Variable, None] instead of set[Variable] to preserve order 

192 outset: dict[Variable, None] = {} 

193 

194 for arg in self.args: 

195 if isinstance(arg, LogicExp): 

196 outset.update(dict.fromkeys(arg.all_inputs_ordered())) 

197 continue 

198 if isinstance(self, BitLogicExp): 

199 if isinstance(arg, Bit): 

200 outset[arg] = None 

201 elif isinstance(arg, BitRegister): 

202 outset[arg] = None 

203 return list(outset) 

204 

205 def __eq__(self, other: object) -> bool: 

206 if not isinstance(other, LogicExp): 206 ↛ 207line 206 didn't jump to line 207 because the condition on line 206 was never true

207 return False 

208 return (self.op == other.op) and (self.args == other.args) 

209 

210 def to_dict(self) -> dict[str, Any]: 

211 """Output JSON serializable nested dictionary.""" 

212 out: dict[str, Any] = {"op": str(self.op)} 

213 args_ser: list[dict | Constant | list[str | int]] = [] 

214 

215 for arg in self.args: 

216 if isinstance(arg, LogicExp): 

217 args_ser.append(arg.to_dict()) 

218 elif isinstance(arg, Constant): 

219 args_ser.append(arg) 

220 elif isinstance(arg, Bit): 

221 args_ser.append(arg.to_list()) 

222 elif isinstance(arg, BitRegister): 222 ↛ 215line 222 didn't jump to line 215 because the condition on line 222 was always true

223 args_ser.append({"name": arg.name, "size": arg.size}) 

224 

225 out["args"] = args_ser 

226 return out 

227 

228 @classmethod 

229 def from_dict(cls, dic: dict[str, Any]) -> "LogicExp": 

230 """Load from JSON serializable nested dictionary.""" 

231 opset_name, op_name = dic["op"].split(".", 2) 

232 opset = BitWiseOp if opset_name == "BitWiseOp" else RegWiseOp 

233 op = next(o for o in opset if o.name == op_name) 

234 args: list[ArgType] = [] 

235 for arg_ser in dic["args"]: 

236 if isinstance(arg_ser, Constant): 

237 args.append(arg_ser) 

238 elif isinstance(arg_ser, list): 

239 args.append(Bit(arg_ser[0], arg_ser[1])) 

240 elif isinstance(arg_ser, dict): 240 ↛ 235line 240 didn't jump to line 235 because the condition on line 240 was always true

241 if "op" in arg_ser: 

242 args.append(LogicExp.from_dict(arg_ser)) 

243 else: 

244 args.append(BitRegister(arg_ser["name"], arg_ser["size"])) 

245 return create_logic_exp(op, args) 

246 

247 def _rename_args_recursive( 

248 self, cmap: dict[Bit, Bit], renamed_regs: set[str] 

249 ) -> bool: 

250 success = False 

251 for i, arg in enumerate(self.args): 

252 if isinstance(arg, Bit): 

253 if arg in cmap: 

254 self.args[i] = cmap[arg] 

255 success = True 

256 elif isinstance(arg, BitRegister): 

257 if arg.name in renamed_regs: 

258 raise ValueError( 

259 f"""Can't rename bits in {arg.__repr__()} """ 

260 """because the register is being used """ 

261 """in a register-wise logic expression.""" 

262 ) 

263 elif isinstance(arg, LogicExp): 

264 success |= arg._rename_args_recursive(cmap, renamed_regs) 

265 return success 

266 

267 def rename_args(self, cmap: dict[Bit, Bit]) -> bool: 

268 """Rename the Bits according to a Bit map. Raise ValueError if 

269 a bit is being used in a register-wise expression. 

270 """ 

271 if all(old_bit == new_bit for old_bit, new_bit in cmap.items()): 

272 return False 

273 renamed_regs = set([key.reg_name for key in cmap]) 

274 return self._rename_args_recursive(cmap, renamed_regs) 

275 

276 

277BitArgType = Union[LogicExp, Bit, Constant] 

278RegArgType = Union[LogicExp, BitRegister, Constant] 

279 

280 

281class BitLogicExp(LogicExp): 

282 """Expression acting only on Bit or Constant types.""" 

283 

284 def __and__(self, other: BitArgType) -> "BitAnd": 

285 return BitAnd(self, other) 

286 

287 def __rand__(self, other: BitArgType) -> "BitAnd": 

288 return BitAnd(self, other) 

289 

290 def __or__(self, other: BitArgType) -> "BitOr": 

291 return BitOr(self, other) 

292 

293 def __ror__(self, other: BitArgType) -> "BitOr": 

294 return BitOr(self, other) 

295 

296 def __xor__(self, other: BitArgType) -> "BitXor": 

297 return BitXor(self, other) 

298 

299 def __rxor__(self, other: BitArgType) -> "BitXor": 

300 return BitXor(self, other) 

301 

302 

303class RegLogicExp(LogicExp): 

304 """Expression acting only on BitRegister or Constant types.""" 

305 

306 def __and__(self, other: RegArgType) -> "RegAnd": 

307 return RegAnd(self, other) 

308 

309 def __rand__(self, other: RegArgType) -> "RegAnd": 

310 return RegAnd(self, other) 

311 

312 def __or__(self, other: RegArgType) -> "RegOr": 

313 return RegOr(self, other) 

314 

315 def __ror__(self, other: RegArgType) -> "RegOr": 

316 return RegOr(self, other) 

317 

318 def __xor__(self, other: RegArgType) -> "RegXor": 

319 return RegXor(self, other) 

320 

321 def __rxor__(self, other: RegArgType) -> "RegXor": 

322 return RegXor(self, other) 

323 

324 def __add__(self, other: RegArgType) -> "RegAdd": 

325 return RegAdd(self, other) 

326 

327 def __sub__(self, other: RegArgType) -> "RegSub": 

328 return RegSub(self, other) 

329 

330 def __mul__(self, other: RegArgType) -> "RegMul": 

331 return RegMul(self, other) 

332 

333 def __floordiv__(self, other: RegArgType) -> "RegDiv": 

334 return RegDiv(self, other) 

335 

336 def __pow__(self, other: RegArgType) -> "RegPow": 

337 return RegPow(self, other) 

338 

339 def __lshift__(self, other: RegArgType) -> "RegLsh": 

340 return RegLsh(self, other) 

341 

342 def __rshift__(self, other: RegArgType) -> "RegRsh": 

343 return RegRsh(self, other) 

344 

345 

346class BinaryOp(LogicExp): 

347 """Expresion for operation on two arguments.""" 

348 

349 def __str__(self) -> str: 

350 return f"({self.args[0]} {self.op.value} {self.args[1]})" 

351 

352 

353class UnaryOp(LogicExp): 

354 """Expression for operation on one argument.""" 

355 

356 def __str__(self) -> str: 

357 return f"({self.op.value} {self.args[0]})" 

358 

359 

360class NullaryOp(LogicExp): 

361 """Expression for operation on no arguments (i.e. constant).""" 

362 

363 def __str__(self) -> str: 

364 return f"({self.op.value})" 

365 

366 

367class And(BinaryOp): 

368 @staticmethod 

369 def _const_eval(args: list[Constant]) -> Constant: 

370 return args[0] & args[1] 

371 

372 def eval_vals(self) -> ArgType: 

373 rval: ArgType = super().eval_vals() 

374 if 0 in self.args: 

375 return 0 

376 return rval 

377 

378 

379class Or(BinaryOp): 

380 @staticmethod 

381 def _const_eval(args: list[Constant]) -> Constant: 

382 return args[0] | args[1] 

383 

384 

385class Xor(BinaryOp): 

386 @staticmethod 

387 def _const_eval(args: list[Constant]) -> Constant: 

388 return args[0] ^ args[1] 

389 

390 

391class BitAnd(And, BitLogicExp): 

392 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None: 

393 self.op = BitWiseOp.AND 

394 self.args = [arg1, arg2] 

395 

396 

397class BitOr(Or, BitLogicExp): 

398 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None: 

399 self.op = BitWiseOp.OR 

400 self.args = [arg1, arg2] 

401 

402 def eval_vals(self) -> ArgType: 

403 rval: ArgType = super().eval_vals() 

404 if 1 in self.args: 

405 return 1 

406 return rval 

407 

408 

409class BitXor(Xor, BitLogicExp): 

410 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None: 

411 self.op = BitWiseOp.XOR 

412 self.args = [arg1, arg2] 

413 

414 

415class BitNot(UnaryOp, BitLogicExp): 

416 def __init__(self, arg1: BitArgType) -> None: 

417 self.op = BitWiseOp.NOT 

418 self.args = [arg1] 

419 

420 @staticmethod 

421 def _const_eval(args: list[Constant]) -> Constant: 

422 return 1 - args[0] 

423 

424 

425class BitZero(NullaryOp, BitLogicExp): 

426 def __init__(self) -> None: 

427 self.op = BitWiseOp.ZERO 

428 self.args = [] 

429 

430 @staticmethod 

431 def _const_eval(args: list[Constant]) -> Constant: 

432 return 0 

433 

434 

435class BitOne(NullaryOp, BitLogicExp): 

436 def __init__(self) -> None: 

437 self.op = BitWiseOp.ONE 

438 self.args = [] 

439 

440 @staticmethod 

441 def _const_eval(args: list[Constant]) -> Constant: 

442 return 1 

443 

444 

445class RegAnd(And, RegLogicExp): 

446 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

447 self.op = RegWiseOp.AND 

448 self.args = [arg1, arg2] 

449 

450 

451class RegOr(Or, RegLogicExp): 

452 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

453 self.op = RegWiseOp.OR 

454 self.args = [arg1, arg2] 

455 

456 

457class RegXor(Xor, RegLogicExp): 

458 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

459 self.op = RegWiseOp.XOR 

460 self.args = [arg1, arg2] 

461 

462 

463class RegAdd(BinaryOp, RegLogicExp): 

464 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

465 self.op = RegWiseOp.ADD 

466 self.args = [arg1, arg2] 

467 

468 

469class RegSub(BinaryOp, RegLogicExp): 

470 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

471 self.op = RegWiseOp.SUB 

472 self.args = [arg1, arg2] 

473 

474 

475class RegMul(BinaryOp, RegLogicExp): 

476 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

477 self.op = RegWiseOp.MUL 

478 self.args = [arg1, arg2] 

479 

480 

481class RegDiv(BinaryOp, RegLogicExp): 

482 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

483 self.op = RegWiseOp.DIV 

484 self.args = [arg1, arg2] 

485 

486 

487class RegPow(BinaryOp, RegLogicExp): 

488 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

489 self.op = RegWiseOp.POW 

490 self.args = [arg1, arg2] 

491 

492 

493class RegLsh(BinaryOp, RegLogicExp): 

494 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

495 self.op = RegWiseOp.LSH 

496 self.args = [arg1, arg2] 

497 

498 

499class RegNeg(UnaryOp, RegLogicExp): 

500 def __init__(self, arg1: RegArgType) -> None: 

501 self.op = RegWiseOp.NEG 

502 self.args = [arg1] 

503 

504 

505class RegNot(UnaryOp, RegLogicExp): 

506 def __init__(self, arg1: RegArgType) -> None: 

507 self.op = RegWiseOp.NOT 

508 self.args = [arg1] 

509 

510 

511class RegRsh(BinaryOp, RegLogicExp): 

512 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

513 self.op = RegWiseOp.RSH 

514 self.args = [arg1, arg2] 

515 

516 

517class PredicateExp(BinaryOp): 

518 """ 

519 A binary predicate where the arguments are either 

520 Bits, BitRegisters, or Constants. 

521 """ 

522 

523 

524class Eq(PredicateExp): 

525 @staticmethod 

526 def _const_eval(args: list[Constant]) -> Constant: 

527 return args[0] == args[1] 

528 

529 

530class Neq(PredicateExp): 

531 @staticmethod 

532 def _const_eval(args: list[Constant]) -> Constant: 

533 return 1 - Eq._const_eval(args) 

534 

535 

536class BitEq(Eq, BitLogicExp): 

537 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None: 

538 self.op = BitWiseOp.EQ 

539 self.args = [arg1, arg2] 

540 

541 

542class BitNeq(Neq, BitLogicExp): 

543 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None: 

544 self.op = BitWiseOp.NEQ 

545 self.args = [arg1, arg2] 

546 

547 

548class RegEq(Eq, RegLogicExp): 

549 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

550 self.op = RegWiseOp.EQ 

551 self.args = [arg1, arg2] 

552 

553 

554class RegNeq(Neq, RegLogicExp): 

555 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

556 self.op = RegWiseOp.NEQ 

557 self.args = [arg1, arg2] 

558 

559 

560class RegLt(PredicateExp, RegLogicExp): 

561 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

562 self.op = RegWiseOp.LT 

563 self.args = [arg1, arg2] 

564 

565 @staticmethod 

566 def _const_eval(args: list[Constant]) -> Constant: 

567 return args[0] < args[1] 

568 

569 

570class RegGt(PredicateExp, RegLogicExp): 

571 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

572 self.op = RegWiseOp.GT 

573 self.args = [arg1, arg2] 

574 

575 @staticmethod 

576 def _const_eval(args: list[Constant]) -> Constant: 

577 return args[0] > args[1] 

578 

579 

580class RegLeq(PredicateExp, RegLogicExp): 

581 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

582 self.op = RegWiseOp.LEQ 

583 self.args = [arg1, arg2] 

584 

585 @staticmethod 

586 def _const_eval(args: list[Constant]) -> Constant: 

587 return args[0] <= args[1] 

588 

589 

590class RegGeq(PredicateExp, RegLogicExp): 

591 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: 

592 self.op = RegWiseOp.GEQ 

593 self.args = [arg1, arg2] 

594 

595 @staticmethod 

596 def _const_eval(args: list[Constant]) -> Constant: 

597 return args[0] >= args[1] 

598 

599 

600def reg_eq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp: 

601 """Function to express a BitRegister equality predicate, i.e. 

602 for a register ``r``, ``(r == 5)`` is expressed as ``reg_eq(r, 5)``""" 

603 return RegEq(register, value) 

604 

605 

606def reg_neq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp: 

607 """Function to express a BitRegister inequality predicate, i.e. 

608 for a register ``r``, ``(r != 5)`` is expressed as ``reg_neq(r, 5)``""" 

609 return RegNeq(register, value) 

610 

611 

612def reg_lt(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp: 

613 """Function to express a BitRegister less than predicate, i.e. 

614 for a register ``r``, ``(r < 5)`` is expressed as ``reg_lt(r, 5)``""" 

615 return RegLt(register, value) 

616 

617 

618def reg_gt(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp: 

619 """Function to express a BitRegister greater than predicate, i.e. 

620 for a register ``r``, ``(r > 5)`` is expressed as ``reg_gt(r, 5)``""" 

621 return RegGt(register, value) 

622 

623 

624def reg_leq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp: 

625 """Function to express a BitRegister less than or equal to predicate, 

626 i.e. for a register ``r``, ``(r <= 5)`` is expressed as ``reg_leq(r, 5)``""" 

627 return RegLeq(register, value) 

628 

629 

630def reg_geq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp: 

631 """Function to express a BitRegister greater than or equal to 

632 predicate, i.e. for a register ``r``, ``(r >= 5)`` is expressed as 

633 ``reg_geq(r, 5)``""" 

634 return RegGeq(register, value) 

635 

636 

637def if_bit(bit: Bit | BitLogicExp) -> PredicateExp: 

638 """Equivalent of ``if bit:``.""" 

639 return BitEq(bit, 1) 

640 

641 

642def if_not_bit(bit: Bit | BitLogicExp) -> PredicateExp: 

643 """Equivalent of ``if not bit:``.""" 

644 return BitEq(bit, 0) 

645 

646 

647def create_bit_logic_exp(op: BitWiseOp, args: Sequence[BitArgType]) -> BitLogicExp: 

648 if op == BitWiseOp.AND: 

649 assert len(args) == 2 

650 return BitAnd(args[0], args[1]) 

651 if op == BitWiseOp.OR: 

652 assert len(args) == 2 

653 return BitOr(args[0], args[1]) 

654 if op == BitWiseOp.XOR: 

655 assert len(args) == 2 

656 return BitXor(args[0], args[1]) 

657 if op == BitWiseOp.NOT: 

658 assert len(args) == 1 

659 return BitNot(args[0]) 

660 if op == BitWiseOp.EQ: 

661 assert len(args) == 2 

662 return BitEq(args[0], args[1]) 

663 if op == BitWiseOp.NEQ: 

664 assert len(args) == 2 

665 return BitNeq(args[0], args[1]) 

666 if op == BitWiseOp.ZERO: 

667 assert len(args) == 0 

668 return BitZero() 

669 if op == BitWiseOp.ONE: # noqa: RET503 669 ↛ exitline 669 didn't return from function 'create_bit_logic_exp' because the condition on line 669 was always true

670 assert len(args) == 0 

671 return BitOne() 

672 

673 

674def create_reg_logic_exp(op: RegWiseOp, args: Sequence[RegArgType]) -> RegLogicExp: 

675 if op == RegWiseOp.AND: 

676 assert len(args) == 2 

677 return RegAnd(args[0], args[1]) 

678 if op == RegWiseOp.OR: 

679 assert len(args) == 2 

680 return RegOr(args[0], args[1]) 

681 if op == RegWiseOp.XOR: 

682 assert len(args) == 2 

683 return RegXor(args[0], args[1]) 

684 if op == RegWiseOp.ADD: 

685 assert len(args) == 2 

686 return RegAdd(args[0], args[1]) 

687 if op == RegWiseOp.SUB: 

688 if len(args) == 2: 688 ↛ 690line 688 didn't jump to line 690 because the condition on line 688 was always true

689 return RegSub(args[0], args[1]) 

690 if len(args) == 1: 

691 return RegNeg(args[0]) 

692 if op == RegWiseOp.NEG: 692 ↛ 693line 692 didn't jump to line 693 because the condition on line 692 was never true

693 assert len(args) == 1 

694 return RegNeg(args[0]) 

695 if op == RegWiseOp.MUL: 

696 assert len(args) == 2 

697 return RegMul(args[0], args[1]) 

698 if op == RegWiseOp.DIV: 

699 assert len(args) == 2 

700 return RegDiv(args[0], args[1]) 

701 if op == RegWiseOp.POW: 

702 assert len(args) == 2 

703 return RegPow(args[0], args[1]) 

704 if op == RegWiseOp.LSH: 

705 assert len(args) == 2 

706 return RegLsh(args[0], args[1]) 

707 if op == RegWiseOp.RSH: 

708 assert len(args) == 2 

709 return RegRsh(args[0], args[1]) 

710 if op == RegWiseOp.EQ: 

711 assert len(args) == 2 

712 return RegEq(args[0], args[1]) 

713 if op == RegWiseOp.NEQ: 

714 assert len(args) == 2 

715 return RegNeq(args[0], args[1]) 

716 if op == RegWiseOp.LT: 

717 assert len(args) == 2 

718 return RegLt(args[0], args[1]) 

719 if op == RegWiseOp.GT: 

720 assert len(args) == 2 

721 return RegGt(args[0], args[1]) 

722 if op == RegWiseOp.LEQ: 

723 assert len(args) == 2 

724 return RegLeq(args[0], args[1]) 

725 if op == RegWiseOp.GEQ: 725 ↛ 726line 725 didn't jump to line 726 because the condition on line 725 was never true

726 assert len(args) == 2 

727 return RegGeq(args[0], args[1]) 

728 if op == RegWiseOp.NOT: 728 ↛ 731line 728 didn't jump to line 731 because the condition on line 728 was always true

729 assert len(args) == 1 

730 return RegNot(args[0]) 

731 raise ValueError("op type not supported") 

732 

733 

734def create_logic_exp(op: Ops, args: Sequence[ArgType]) -> LogicExp: 

735 if isinstance(op, BitWiseOp): 

736 bit_args = [] 

737 for arg in args: 

738 assert isinstance(arg, (BitLogicExp, Bit, Constant)) 

739 bit_args.append(arg) 

740 return create_bit_logic_exp(op, bit_args) 

741 assert isinstance(op, RegWiseOp) 

742 reg_args = [] 

743 for arg in args: 

744 assert isinstance(arg, (RegLogicExp, BitRegister, Constant)) 

745 reg_args.append(arg) 

746 return create_reg_logic_exp(op, reg_args) 

747 

748 

749def create_predicate_exp(op: Ops, args: Sequence[ArgType]) -> PredicateExp: 

750 if op == BitWiseOp.EQ: 

751 assert len(args) == 2 

752 assert isinstance(args[0], (BitLogicExp, Bit, int)) 

753 assert isinstance(args[1], (BitLogicExp, Bit, int)) 

754 return BitEq(args[0], args[1]) 

755 if op == BitWiseOp.NEQ: 755 ↛ 756line 755 didn't jump to line 756 because the condition on line 755 was never true

756 assert len(args) == 2 

757 assert isinstance(args[0], (BitLogicExp, Bit, int)) 

758 assert isinstance(args[1], (BitLogicExp, Bit, int)) 

759 return BitNeq(args[0], args[1]) 

760 if op == RegWiseOp.EQ: 

761 assert len(args) == 2 

762 assert isinstance(args[0], (RegLogicExp, BitRegister, int)) 

763 assert isinstance(args[1], (RegLogicExp, BitRegister, int)) 

764 return RegEq(args[0], args[1]) 

765 if op == RegWiseOp.NEQ: 

766 assert len(args) == 2 

767 assert isinstance(args[0], (RegLogicExp, BitRegister, int)) 

768 assert isinstance(args[1], (RegLogicExp, BitRegister, int)) 

769 return RegNeq(args[0], args[1]) 

770 if op == RegWiseOp.LT: 

771 assert len(args) == 2 

772 assert isinstance(args[0], (RegLogicExp, BitRegister, int)) 

773 assert isinstance(args[1], (RegLogicExp, BitRegister, int)) 

774 return RegLt(args[0], args[1]) 

775 if op == RegWiseOp.GT: 

776 assert len(args) == 2 

777 assert isinstance(args[0], (RegLogicExp, BitRegister, int)) 

778 assert isinstance(args[1], (RegLogicExp, BitRegister, int)) 

779 return RegGt(args[0], args[1]) 

780 if op == RegWiseOp.LEQ: 

781 assert len(args) == 2 

782 assert isinstance(args[0], (RegLogicExp, BitRegister, int)) 

783 assert isinstance(args[1], (RegLogicExp, BitRegister, int)) 

784 return RegLeq(args[0], args[1]) 

785 if op == RegWiseOp.GEQ: 785 ↛ 790line 785 didn't jump to line 790 because the condition on line 785 was always true

786 assert len(args) == 2 

787 assert isinstance(args[0], (RegLogicExp, BitRegister, int)) 

788 assert isinstance(args[1], (RegLogicExp, BitRegister, int)) 

789 return RegGeq(args[0], args[1]) 

790 raise ValueError("op type not supported")