Coverage for /home/runner/work/tket/tket/pytket/pytket/circuit/logic_exp.py: 90%
555 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.
15"""Classes and functions for constructing logical
16expressions over Bit and BitRegister."""
18import contextlib
19from collections.abc import Iterable, Iterator, Sequence
20from dataclasses import dataclass
21from enum import Enum
22from typing import Any, ClassVar, TypeVar, Union, cast
24from pytket.circuit import Bit, BitRegister
26T = TypeVar("T")
29def filter_by_type(seq: Iterable, var_type: type[T]) -> Iterator[tuple[int, T]]:
30 """Return enumeration of seq, with only elements of type var_type."""
31 return filter(lambda x: isinstance(x[1], var_type), enumerate(seq))
34class BitWiseOp(Enum):
35 """Enum for operations on Bit."""
37 AND = "&"
38 OR = "|"
39 XOR = "^"
40 EQ = "=="
41 NEQ = "!="
42 NOT = "~"
43 ZERO = "0"
44 ONE = "1"
47class RegWiseOp(Enum):
48 """Enum for operations on BitRegister."""
50 AND = "&"
51 OR = "|"
52 XOR = "^"
53 EQ = "=="
54 NEQ = "!="
55 LT = "<"
56 GT = ">"
57 LEQ = "<="
58 GEQ = ">="
59 ADD = "+"
60 SUB = "-"
61 MUL = "*"
62 DIV = "/"
63 POW = "**"
64 LSH = "<<"
65 RSH = ">>"
66 NOT = "~"
67 NEG = "-" # noqa: PIE796
70Ops = Union[BitWiseOp, RegWiseOp] # all op enum types # noqa: UP007
73Constant = int # constants in expression
74Variable = Union[Bit, BitRegister] # variables in expression # noqa: UP007
75ArgType = Union["LogicExp", Variable, Constant] # all possible arguments in expression
78@dataclass(init=False)
79class LogicExp:
80 """Logical expressions over Bit or BitRegister.
81 Encoded as a tree of expressions"""
83 op: Ops # enum for operation encoded by this node
84 args: list[ArgType] # arguments of operation
85 # class level dictionary mapping enum to class
86 op_cls_dict: ClassVar[dict[Ops, type["LogicExp"]]] = {}
88 @classmethod
89 def factory(cls, op: Ops) -> type["LogicExp"]: # noqa: PLR0911, PLR0912
90 """Return matching operation class for enum."""
91 # RegNeg cannot be initialised this way as "-" clashes with SUB
92 if op == BitWiseOp.AND:
93 return BitAnd
94 if op == BitWiseOp.OR:
95 return BitOr
96 if op == BitWiseOp.XOR:
97 return BitXor
98 if op == BitWiseOp.NOT:
99 return BitNot
100 if op == BitWiseOp.EQ:
101 return BitEq
102 if op == BitWiseOp.NEQ:
103 return BitNeq
104 if op == BitWiseOp.ZERO:
105 return BitZero
106 if op == BitWiseOp.ONE:
107 return BitOne
108 if op == RegWiseOp.AND:
109 return RegAnd
110 if op == RegWiseOp.OR:
111 return RegOr
112 if op == RegWiseOp.XOR:
113 return RegXor
114 if op == RegWiseOp.ADD:
115 return RegAdd
116 if op == RegWiseOp.SUB:
117 return RegSub
118 if op == RegWiseOp.MUL:
119 return RegMul
120 if op == RegWiseOp.DIV:
121 return RegDiv
122 if op == RegWiseOp.POW:
123 return RegPow
124 if op == RegWiseOp.LSH:
125 return RegLsh
126 if op == RegWiseOp.RSH:
127 return RegRsh
128 if op == RegWiseOp.EQ:
129 return RegEq
130 if op == RegWiseOp.NEQ:
131 return RegNeq
132 if op == RegWiseOp.LT:
133 return RegLt
134 if op == RegWiseOp.GT: 134 ↛ 135line 134 didn't jump to line 135 because the condition on line 134 was never true
135 return RegGt
136 if op == RegWiseOp.LEQ:
137 return RegLeq
138 if op == RegWiseOp.GEQ:
139 return RegGeq
140 if op == RegWiseOp.NOT: 140 ↛ 142line 140 didn't jump to line 142 because the condition on line 140 was always true
141 return RegNot
142 raise ValueError("op type not supported")
144 def set_value(self, var: Variable, val: Constant) -> None:
145 """Set value of var to val recursively."""
146 for i, arg in enumerate(self.args):
147 if isinstance(arg, Bit | BitRegister):
148 if arg == var:
149 self.args[i] = val
150 elif isinstance(arg, LogicExp): 150 ↛ 151line 150 didn't jump to line 151 because the condition on line 150 was never true
151 arg.set_value(var, val)
153 @staticmethod
154 def _const_eval(args: list[Constant]) -> Constant:
155 """Evaluate expression given constant values for all args."""
156 raise NotImplementedError
158 def eval_vals(self) -> ArgType:
159 """Attempt to evaluate all sub-expressions; simple constant folding."""
160 rval: ArgType = self
161 for i, arg in filter_by_type(self.args, LogicExp): 161 ↛ 162line 161 didn't jump to line 162 because the loop on line 161 never started
162 self.args[i] = arg.eval_vals()
163 if all(isinstance(a, Constant) for a in self.args): 163 ↛ 166line 163 didn't jump to line 166 because the condition on line 163 was always true
164 with contextlib.suppress(NotImplementedError):
165 rval = self._const_eval(cast("list[Constant]", self.args))
166 return rval
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()
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
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] = {}
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)
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)
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]] = []
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})
225 out["args"] = args_ser
226 return out
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)
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) # noqa: SLF001
265 return success
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 = {key.reg_name for key in cmap}
274 return self._rename_args_recursive(cmap, renamed_regs)
277BitArgType = Union[LogicExp, Bit, Constant] # noqa: UP007
278RegArgType = Union[LogicExp, BitRegister, Constant] # noqa: UP007
281class BitLogicExp(LogicExp):
282 """Expression acting only on Bit or Constant types."""
284 def __and__(self, other: BitArgType) -> "BitAnd":
285 return BitAnd(self, other)
287 def __rand__(self, other: BitArgType) -> "BitAnd":
288 return BitAnd(self, other)
290 def __or__(self, other: BitArgType) -> "BitOr":
291 return BitOr(self, other)
293 def __ror__(self, other: BitArgType) -> "BitOr":
294 return BitOr(self, other)
296 def __xor__(self, other: BitArgType) -> "BitXor":
297 return BitXor(self, other)
299 def __rxor__(self, other: BitArgType) -> "BitXor":
300 return BitXor(self, other)
303class RegLogicExp(LogicExp):
304 """Expression acting only on BitRegister or Constant types."""
306 def __and__(self, other: RegArgType) -> "RegAnd":
307 return RegAnd(self, other)
309 def __rand__(self, other: RegArgType) -> "RegAnd":
310 return RegAnd(self, other)
312 def __or__(self, other: RegArgType) -> "RegOr":
313 return RegOr(self, other)
315 def __ror__(self, other: RegArgType) -> "RegOr":
316 return RegOr(self, other)
318 def __xor__(self, other: RegArgType) -> "RegXor":
319 return RegXor(self, other)
321 def __rxor__(self, other: RegArgType) -> "RegXor":
322 return RegXor(self, other)
324 def __add__(self, other: RegArgType) -> "RegAdd":
325 return RegAdd(self, other)
327 def __sub__(self, other: RegArgType) -> "RegSub":
328 return RegSub(self, other)
330 def __mul__(self, other: RegArgType) -> "RegMul":
331 return RegMul(self, other)
333 def __floordiv__(self, other: RegArgType) -> "RegDiv":
334 return RegDiv(self, other)
336 def __pow__(self, other: RegArgType) -> "RegPow":
337 return RegPow(self, other)
339 def __lshift__(self, other: RegArgType) -> "RegLsh":
340 return RegLsh(self, other)
342 def __rshift__(self, other: RegArgType) -> "RegRsh":
343 return RegRsh(self, other)
346class BinaryOp(LogicExp):
347 """Expresion for operation on two arguments."""
349 def __str__(self) -> str:
350 return f"({self.args[0]} {self.op.value} {self.args[1]})"
353class UnaryOp(LogicExp):
354 """Expression for operation on one argument."""
356 def __str__(self) -> str:
357 return f"({self.op.value} {self.args[0]})"
360class NullaryOp(LogicExp):
361 """Expression for operation on no arguments (i.e. constant)."""
363 def __str__(self) -> str:
364 return f"({self.op.value})"
367class And(BinaryOp):
368 @staticmethod
369 def _const_eval(args: list[Constant]) -> Constant:
370 return args[0] & args[1]
372 def eval_vals(self) -> ArgType:
373 rval: ArgType = super().eval_vals()
374 if 0 in self.args:
375 return 0
376 return rval
379class Or(BinaryOp):
380 @staticmethod
381 def _const_eval(args: list[Constant]) -> Constant:
382 return args[0] | args[1]
385class Xor(BinaryOp):
386 @staticmethod
387 def _const_eval(args: list[Constant]) -> Constant:
388 return args[0] ^ args[1]
391class BitAnd(And, BitLogicExp):
392 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
393 self.op = BitWiseOp.AND
394 self.args = [arg1, arg2]
397class BitOr(Or, BitLogicExp):
398 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
399 self.op = BitWiseOp.OR
400 self.args = [arg1, arg2]
402 def eval_vals(self) -> ArgType:
403 rval: ArgType = super().eval_vals()
404 if 1 in self.args: 404 ↛ 406line 404 didn't jump to line 406 because the condition on line 404 was always true
405 return 1
406 return rval
409class BitXor(Xor, BitLogicExp):
410 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
411 self.op = BitWiseOp.XOR
412 self.args = [arg1, arg2]
415class BitNot(UnaryOp, BitLogicExp):
416 def __init__(self, arg1: BitArgType) -> None:
417 self.op = BitWiseOp.NOT
418 self.args = [arg1]
420 @staticmethod
421 def _const_eval(args: list[Constant]) -> Constant:
422 return 1 - args[0]
425class BitZero(NullaryOp, BitLogicExp):
426 def __init__(self) -> None:
427 self.op = BitWiseOp.ZERO
428 self.args = []
430 @staticmethod
431 def _const_eval(args: list[Constant]) -> Constant:
432 return 0
435class BitOne(NullaryOp, BitLogicExp):
436 def __init__(self) -> None:
437 self.op = BitWiseOp.ONE
438 self.args = []
440 @staticmethod
441 def _const_eval(args: list[Constant]) -> Constant:
442 return 1
445class RegAnd(And, RegLogicExp):
446 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
447 self.op = RegWiseOp.AND
448 self.args = [arg1, arg2]
451class RegOr(Or, RegLogicExp):
452 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
453 self.op = RegWiseOp.OR
454 self.args = [arg1, arg2]
457class RegXor(Xor, RegLogicExp):
458 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
459 self.op = RegWiseOp.XOR
460 self.args = [arg1, arg2]
463class RegAdd(BinaryOp, RegLogicExp):
464 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
465 self.op = RegWiseOp.ADD
466 self.args = [arg1, arg2]
469class RegSub(BinaryOp, RegLogicExp):
470 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
471 self.op = RegWiseOp.SUB
472 self.args = [arg1, arg2]
475class RegMul(BinaryOp, RegLogicExp):
476 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
477 self.op = RegWiseOp.MUL
478 self.args = [arg1, arg2]
481class RegDiv(BinaryOp, RegLogicExp):
482 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
483 self.op = RegWiseOp.DIV
484 self.args = [arg1, arg2]
487class RegPow(BinaryOp, RegLogicExp):
488 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
489 self.op = RegWiseOp.POW
490 self.args = [arg1, arg2]
493class RegLsh(BinaryOp, RegLogicExp):
494 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
495 self.op = RegWiseOp.LSH
496 self.args = [arg1, arg2]
499class RegNeg(UnaryOp, RegLogicExp):
500 def __init__(self, arg1: RegArgType) -> None:
501 self.op = RegWiseOp.NEG
502 self.args = [arg1]
505class RegNot(UnaryOp, RegLogicExp):
506 def __init__(self, arg1: RegArgType) -> None:
507 self.op = RegWiseOp.NOT
508 self.args = [arg1]
511class RegRsh(BinaryOp, RegLogicExp):
512 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
513 self.op = RegWiseOp.RSH
514 self.args = [arg1, arg2]
517class PredicateExp(BinaryOp):
518 """
519 A binary predicate where the arguments are either
520 Bits, BitRegisters, or Constants.
521 """
524class Eq(PredicateExp):
525 @staticmethod
526 def _const_eval(args: list[Constant]) -> Constant:
527 return args[0] == args[1]
530class Neq(PredicateExp):
531 @staticmethod
532 def _const_eval(args: list[Constant]) -> Constant:
533 return 1 - Eq._const_eval(args) # noqa: SLF001
536class BitEq(Eq, BitLogicExp):
537 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
538 self.op = BitWiseOp.EQ
539 self.args = [arg1, arg2]
542class BitNeq(Neq, BitLogicExp):
543 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
544 self.op = BitWiseOp.NEQ
545 self.args = [arg1, arg2]
548class RegEq(Eq, RegLogicExp):
549 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
550 self.op = RegWiseOp.EQ
551 self.args = [arg1, arg2]
554class RegNeq(Neq, RegLogicExp):
555 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
556 self.op = RegWiseOp.NEQ
557 self.args = [arg1, arg2]
560class RegLt(PredicateExp, RegLogicExp):
561 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
562 self.op = RegWiseOp.LT
563 self.args = [arg1, arg2]
565 @staticmethod
566 def _const_eval(args: list[Constant]) -> Constant:
567 return args[0] < args[1]
570class RegGt(PredicateExp, RegLogicExp):
571 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
572 self.op = RegWiseOp.GT
573 self.args = [arg1, arg2]
575 @staticmethod
576 def _const_eval(args: list[Constant]) -> Constant:
577 return args[0] > args[1]
580class RegLeq(PredicateExp, RegLogicExp):
581 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
582 self.op = RegWiseOp.LEQ
583 self.args = [arg1, arg2]
585 @staticmethod
586 def _const_eval(args: list[Constant]) -> Constant:
587 return args[0] <= args[1]
590class RegGeq(PredicateExp, RegLogicExp):
591 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
592 self.op = RegWiseOp.GEQ
593 self.args = [arg1, arg2]
595 @staticmethod
596 def _const_eval(args: list[Constant]) -> Constant:
597 return args[0] >= args[1]
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)
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)
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)
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)
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)
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)
637def if_bit(bit: Bit | BitLogicExp) -> PredicateExp:
638 """Equivalent of ``if bit:``."""
639 return BitEq(bit, 1)
642def if_not_bit(bit: Bit | BitLogicExp) -> PredicateExp:
643 """Equivalent of ``if not bit:``."""
644 return BitEq(bit, 0)
647def create_bit_logic_exp(op: BitWiseOp, args: Sequence[BitArgType]) -> BitLogicExp: # noqa: PLR0911
648 """
649 Builds the :py:class:`LogicExp` corresponding to applying the given
650 :py:class:`BitWiseOp` to some sequence of bits.
651 """
652 if op == BitWiseOp.AND:
653 assert len(args) == 2 # noqa: PLR2004
654 return BitAnd(args[0], args[1])
655 if op == BitWiseOp.OR:
656 assert len(args) == 2 # noqa: PLR2004
657 return BitOr(args[0], args[1])
658 if op == BitWiseOp.XOR:
659 assert len(args) == 2 # noqa: PLR2004
660 return BitXor(args[0], args[1])
661 if op == BitWiseOp.NOT:
662 assert len(args) == 1
663 return BitNot(args[0])
664 if op == BitWiseOp.EQ:
665 assert len(args) == 2 # noqa: PLR2004
666 return BitEq(args[0], args[1])
667 if op == BitWiseOp.NEQ:
668 assert len(args) == 2 # noqa: PLR2004
669 return BitNeq(args[0], args[1])
670 if op == BitWiseOp.ZERO:
671 assert len(args) == 0
672 return BitZero()
673 if op == BitWiseOp.ONE: # noqa: RET503 673 ↛ exitline 673 didn't return from function 'create_bit_logic_exp' because the condition on line 673 was always true
674 assert len(args) == 0
675 return BitOne()
678def create_reg_logic_exp(op: RegWiseOp, args: Sequence[RegArgType]) -> RegLogicExp: # noqa: PLR0911, PLR0912
679 """
680 Builds the :py:class:`LogicExp` corresponding to applying the given
681 :py:class:`RegWiseOp` to some sequence of registers.
682 """
683 if op == RegWiseOp.AND:
684 assert len(args) == 2 # noqa: PLR2004
685 return RegAnd(args[0], args[1])
686 if op == RegWiseOp.OR:
687 assert len(args) == 2 # noqa: PLR2004
688 return RegOr(args[0], args[1])
689 if op == RegWiseOp.XOR:
690 assert len(args) == 2 # noqa: PLR2004
691 return RegXor(args[0], args[1])
692 if op == RegWiseOp.ADD:
693 assert len(args) == 2 # noqa: PLR2004
694 return RegAdd(args[0], args[1])
695 if op == RegWiseOp.SUB:
696 if len(args) == 2: # noqa: PLR2004 696 ↛ 698line 696 didn't jump to line 698 because the condition on line 696 was always true
697 return RegSub(args[0], args[1])
698 if len(args) == 1:
699 return RegNeg(args[0])
700 if op == RegWiseOp.NEG: 700 ↛ 701line 700 didn't jump to line 701 because the condition on line 700 was never true
701 assert len(args) == 1
702 return RegNeg(args[0])
703 if op == RegWiseOp.MUL:
704 assert len(args) == 2 # noqa: PLR2004
705 return RegMul(args[0], args[1])
706 if op == RegWiseOp.DIV:
707 assert len(args) == 2 # noqa: PLR2004
708 return RegDiv(args[0], args[1])
709 if op == RegWiseOp.POW:
710 assert len(args) == 2 # noqa: PLR2004
711 return RegPow(args[0], args[1])
712 if op == RegWiseOp.LSH:
713 assert len(args) == 2 # noqa: PLR2004
714 return RegLsh(args[0], args[1])
715 if op == RegWiseOp.RSH:
716 assert len(args) == 2 # noqa: PLR2004
717 return RegRsh(args[0], args[1])
718 if op == RegWiseOp.EQ:
719 assert len(args) == 2 # noqa: PLR2004
720 return RegEq(args[0], args[1])
721 if op == RegWiseOp.NEQ:
722 assert len(args) == 2 # noqa: PLR2004
723 return RegNeq(args[0], args[1])
724 if op == RegWiseOp.LT:
725 assert len(args) == 2 # noqa: PLR2004
726 return RegLt(args[0], args[1])
727 if op == RegWiseOp.GT:
728 assert len(args) == 2 # noqa: PLR2004
729 return RegGt(args[0], args[1])
730 if op == RegWiseOp.LEQ:
731 assert len(args) == 2 # noqa: PLR2004
732 return RegLeq(args[0], args[1])
733 if op == RegWiseOp.GEQ:
734 assert len(args) == 2 # noqa: PLR2004
735 return RegGeq(args[0], args[1])
736 if op == RegWiseOp.NOT: 736 ↛ 739line 736 didn't jump to line 739 because the condition on line 736 was always true
737 assert len(args) == 1
738 return RegNot(args[0])
739 raise ValueError("op type not supported")
742def create_logic_exp(op: Ops, args: Sequence[ArgType]) -> LogicExp:
743 """
744 Builds the :py:class:`LogicExp` corresponding to applying the given
745 :py:class:`BitWiseOp` or :py:class:`RegWiseOp` to some sequence of
746 arguments.
747 """
748 if isinstance(op, BitWiseOp):
749 bit_args = []
750 args = [
751 arg[0] if isinstance(arg, BitRegister) and arg.size == 1 else arg
752 for arg in args
753 ]
754 for arg in args:
755 assert isinstance(arg, BitLogicExp | Bit | Constant)
756 bit_args.append(arg)
757 return create_bit_logic_exp(op, bit_args)
758 assert isinstance(op, RegWiseOp)
759 reg_args = []
760 for arg in args:
761 assert isinstance(arg, RegLogicExp | BitRegister | Constant)
762 reg_args.append(arg)
763 return create_reg_logic_exp(op, reg_args)
766def create_predicate_exp(op: Ops, args: Sequence[ArgType]) -> PredicateExp: # noqa: PLR0911
767 """
768 Builds the :py:class:`LogicExp` corresponding to applying a given
769 comparison predicate to some sequence of arguments.
770 """
771 if op == BitWiseOp.EQ:
772 assert len(args) == 2 # noqa: PLR2004
773 assert isinstance(args[0], BitLogicExp | Bit | int)
774 assert isinstance(args[1], BitLogicExp | Bit | int)
775 return BitEq(args[0], args[1])
776 if op == BitWiseOp.NEQ:
777 assert len(args) == 2 # noqa: PLR2004
778 assert isinstance(args[0], BitLogicExp | Bit | int)
779 assert isinstance(args[1], BitLogicExp | Bit | int)
780 return BitNeq(args[0], args[1])
781 if op == RegWiseOp.EQ:
782 assert len(args) == 2 # noqa: PLR2004
783 assert isinstance(args[0], RegLogicExp | BitRegister | int)
784 assert isinstance(args[1], RegLogicExp | BitRegister | int)
785 return RegEq(args[0], args[1])
786 if op == RegWiseOp.NEQ:
787 assert len(args) == 2 # noqa: PLR2004
788 assert isinstance(args[0], RegLogicExp | BitRegister | int)
789 assert isinstance(args[1], RegLogicExp | BitRegister | int)
790 return RegNeq(args[0], args[1])
791 if op == RegWiseOp.LT:
792 assert len(args) == 2 # noqa: PLR2004
793 assert isinstance(args[0], RegLogicExp | BitRegister | int)
794 assert isinstance(args[1], RegLogicExp | BitRegister | int)
795 return RegLt(args[0], args[1])
796 if op == RegWiseOp.GT:
797 assert len(args) == 2 # noqa: PLR2004
798 assert isinstance(args[0], RegLogicExp | BitRegister | int)
799 assert isinstance(args[1], RegLogicExp | BitRegister | int)
800 return RegGt(args[0], args[1])
801 if op == RegWiseOp.LEQ:
802 assert len(args) == 2 # noqa: PLR2004
803 assert isinstance(args[0], RegLogicExp | BitRegister | int)
804 assert isinstance(args[1], RegLogicExp | BitRegister | int)
805 return RegLeq(args[0], args[1])
806 if op == RegWiseOp.GEQ: 806 ↛ 811line 806 didn't jump to line 811 because the condition on line 806 was always true
807 assert len(args) == 2 # noqa: PLR2004
808 assert isinstance(args[0], RegLogicExp | BitRegister | int)
809 assert isinstance(args[1], RegLogicExp | BitRegister | int)
810 return RegGeq(args[0], args[1])
811 raise ValueError("op type not supported")