Coverage for /home/runner/work/tket/tket/pytket/pytket/circuit/logic_exp.py: 90%
558 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-02 13:01 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-02 13:01 +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[
76 "LogicExp", Bit | BitRegister, Constant
77] # all possible arguments in expression
80@dataclass(init=False)
81class LogicExp:
82 """Logical expressions over Bit or BitRegister.
83 Encoded as a tree of expressions"""
85 op: Ops # enum for operation encoded by this node
86 args: list[ArgType] # arguments of operation
87 # class level dictionary mapping enum to class
88 op_cls_dict: ClassVar[dict[Ops, type["LogicExp"]]] = {}
90 @classmethod
91 def factory(cls, op: Ops) -> type["LogicExp"]: # noqa: PLR0911, PLR0912
92 """Return matching operation class for enum."""
93 # RegNeg cannot be initialised this way as "-" clashes with SUB
94 if op == BitWiseOp.AND:
95 return BitAnd
96 if op == BitWiseOp.OR:
97 return BitOr
98 if op == BitWiseOp.XOR:
99 return BitXor
100 if op == BitWiseOp.NOT:
101 return BitNot
102 if op == BitWiseOp.EQ:
103 return BitEq
104 if op == BitWiseOp.NEQ:
105 return BitNeq
106 if op == BitWiseOp.ZERO:
107 return BitZero
108 if op == BitWiseOp.ONE:
109 return BitOne
110 if op == RegWiseOp.AND:
111 return RegAnd
112 if op == RegWiseOp.OR:
113 return RegOr
114 if op == RegWiseOp.XOR:
115 return RegXor
116 if op == RegWiseOp.ADD:
117 return RegAdd
118 if op == RegWiseOp.SUB:
119 return RegSub
120 if op == RegWiseOp.MUL:
121 return RegMul
122 if op == RegWiseOp.DIV:
123 return RegDiv
124 if op == RegWiseOp.POW:
125 return RegPow
126 if op == RegWiseOp.LSH:
127 return RegLsh
128 if op == RegWiseOp.RSH:
129 return RegRsh
130 if op == RegWiseOp.EQ:
131 return RegEq
132 if op == RegWiseOp.NEQ:
133 return RegNeq
134 if op == RegWiseOp.LT:
135 return RegLt
136 if op == RegWiseOp.GT: 136 ↛ 137line 136 didn't jump to line 137 because the condition on line 136 was never true
137 return RegGt
138 if op == RegWiseOp.LEQ:
139 return RegLeq
140 if op == RegWiseOp.GEQ:
141 return RegGeq
142 if op == RegWiseOp.NOT: 142 ↛ 144line 142 didn't jump to line 144 because the condition on line 142 was always true
143 return RegNot
144 raise ValueError("op type not supported")
146 def set_value(self, var: Bit | BitRegister, val: Constant) -> None:
147 """Set value of var to val recursively."""
148 for i, arg in enumerate(self.args):
149 if isinstance(arg, Bit | BitRegister):
150 if arg == var:
151 self.args[i] = val
152 elif isinstance(arg, LogicExp): 152 ↛ 153line 152 didn't jump to line 153 because the condition on line 152 was never true
153 arg.set_value(var, val)
155 @staticmethod
156 def _const_eval(args: list[Constant]) -> Constant:
157 """Evaluate expression given constant values for all args."""
158 raise NotImplementedError
160 def eval_vals(self) -> ArgType:
161 """Attempt to evaluate all sub-expressions; simple constant folding."""
162 rval: ArgType = self
163 for i, arg in filter_by_type(self.args, LogicExp): 163 ↛ 164line 163 didn't jump to line 164 because the loop on line 163 never started
164 self.args[i] = arg.eval_vals()
165 if all(isinstance(a, Constant) for a in self.args): 165 ↛ 168line 165 didn't jump to line 168 because the condition on line 165 was always true
166 with contextlib.suppress(NotImplementedError):
167 rval = self._const_eval(cast("list[Constant]", self.args))
168 return rval
170 def all_inputs(self) -> set[Bit | BitRegister]:
171 """
172 :return: All variables involved in expression.
173 """
174 outset: set[Bit | BitRegister] = set()
176 for arg in self.args:
177 if isinstance(arg, LogicExp):
178 outset.update(arg.all_inputs())
179 continue
180 if isinstance(self, BitLogicExp):
181 if isinstance(arg, Bit):
182 outset.add(arg)
183 elif isinstance(arg, BitRegister):
184 outset.add(arg)
185 return outset
187 def all_inputs_ordered(self) -> list[Bit | BitRegister]:
188 """
189 :return: All variables involved in expression, in order of first appearance.
190 """
191 # use dict[Union[Bit, BitRegister], None] instead of set[Union[Bit, BitRegister]] to preserve order
192 outset: dict[Bit | BitRegister, 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 __hash__(self) -> int:
211 return hash((self.op, self.args))
213 def to_dict(self) -> dict[str, Any]:
214 """Output JSON serializable nested dictionary."""
215 out: dict[str, Any] = {"op": str(self.op)}
216 args_ser: list[dict | Constant | list[str | int]] = []
218 for arg in self.args:
219 if isinstance(arg, LogicExp):
220 args_ser.append(arg.to_dict())
221 elif isinstance(arg, Constant):
222 args_ser.append(arg)
223 elif isinstance(arg, Bit):
224 args_ser.append(arg.to_list())
225 elif isinstance(arg, BitRegister): 225 ↛ 218line 225 didn't jump to line 218 because the condition on line 225 was always true
226 args_ser.append({"name": arg.name, "size": arg.size})
228 out["args"] = args_ser
229 return out
231 @classmethod
232 def from_dict(cls, dic: dict[str, Any]) -> "LogicExp":
233 """Load from JSON serializable nested dictionary."""
234 opset_name, op_name = dic["op"].split(".", 2)
235 opset = BitWiseOp if opset_name == "BitWiseOp" else RegWiseOp
236 op = next(o for o in opset if o.name == op_name)
237 args: list[ArgType] = []
238 for arg_ser in dic["args"]:
239 if isinstance(arg_ser, Constant):
240 args.append(arg_ser)
241 elif isinstance(arg_ser, list):
242 args.append(Bit(arg_ser[0], arg_ser[1]))
243 elif isinstance(arg_ser, dict): 243 ↛ 238line 243 didn't jump to line 238 because the condition on line 243 was always true
244 if "op" in arg_ser:
245 args.append(LogicExp.from_dict(arg_ser))
246 else:
247 args.append(BitRegister(arg_ser["name"], arg_ser["size"]))
248 return create_logic_exp(op, args)
250 def _rename_args_recursive(
251 self, cmap: dict[Bit, Bit], renamed_regs: set[str]
252 ) -> bool:
253 success = False
254 for i, arg in enumerate(self.args):
255 if isinstance(arg, Bit):
256 if arg in cmap:
257 self.args[i] = cmap[arg]
258 success = True
259 elif isinstance(arg, BitRegister):
260 if arg.name in renamed_regs:
261 raise ValueError(
262 f"""Can't rename bits in {arg.__repr__()} """
263 """because the register is being used """
264 """in a register-wise logic expression."""
265 )
266 elif isinstance(arg, LogicExp):
267 success |= arg._rename_args_recursive( # noqa: SLF001
268 cmap, renamed_regs
269 )
270 return success
272 def rename_args(self, cmap: dict[Bit, Bit]) -> bool:
273 """Rename the Bits according to a Bit map. Raise ValueError if
274 a bit is being used in a register-wise expression.
275 """
276 if all(old_bit == new_bit for old_bit, new_bit in cmap.items()):
277 return False
278 renamed_regs = {key.reg_name for key in cmap}
279 return self._rename_args_recursive(cmap, renamed_regs)
282BitArgType = Union[LogicExp, Bit, Constant] # noqa: UP007
283RegArgType = Union[LogicExp, BitRegister, Constant] # noqa: UP007
286class BitLogicExp(LogicExp):
287 """Expression acting only on Bit or Constant types."""
289 def __and__(self, other: BitArgType) -> "BitAnd":
290 return BitAnd(self, other)
292 def __rand__(self, other: BitArgType) -> "BitAnd":
293 return BitAnd(self, other)
295 def __or__(self, other: BitArgType) -> "BitOr":
296 return BitOr(self, other)
298 def __ror__(self, other: BitArgType) -> "BitOr":
299 return BitOr(self, other)
301 def __xor__(self, other: BitArgType) -> "BitXor":
302 return BitXor(self, other)
304 def __rxor__(self, other: BitArgType) -> "BitXor":
305 return BitXor(self, other)
308class RegLogicExp(LogicExp):
309 """Expression acting only on BitRegister or Constant types."""
311 def __and__(self, other: RegArgType) -> "RegAnd":
312 return RegAnd(self, other)
314 def __rand__(self, other: RegArgType) -> "RegAnd":
315 return RegAnd(self, other)
317 def __or__(self, other: RegArgType) -> "RegOr":
318 return RegOr(self, other)
320 def __ror__(self, other: RegArgType) -> "RegOr":
321 return RegOr(self, other)
323 def __xor__(self, other: RegArgType) -> "RegXor":
324 return RegXor(self, other)
326 def __rxor__(self, other: RegArgType) -> "RegXor":
327 return RegXor(self, other)
329 def __add__(self, other: RegArgType) -> "RegAdd":
330 return RegAdd(self, other)
332 def __sub__(self, other: RegArgType) -> "RegSub":
333 return RegSub(self, other)
335 def __mul__(self, other: RegArgType) -> "RegMul":
336 return RegMul(self, other)
338 def __floordiv__(self, other: RegArgType) -> "RegDiv":
339 return RegDiv(self, other)
341 def __pow__(self, other: RegArgType) -> "RegPow":
342 return RegPow(self, other)
344 def __lshift__(self, other: RegArgType) -> "RegLsh":
345 return RegLsh(self, other)
347 def __rshift__(self, other: RegArgType) -> "RegRsh":
348 return RegRsh(self, other)
351class BinaryOp(LogicExp):
352 """Expression for operation on two arguments."""
354 def __str__(self) -> str:
355 return f"({self.args[0]} {self.op.value} {self.args[1]})"
358class UnaryOp(LogicExp):
359 """Expression for operation on one argument."""
361 def __str__(self) -> str:
362 return f"({self.op.value} {self.args[0]})"
365class NullaryOp(LogicExp):
366 """Expression for operation on no arguments (i.e. constant)."""
368 def __str__(self) -> str:
369 return f"({self.op.value})"
372class And(BinaryOp):
373 @staticmethod
374 def _const_eval(args: list[Constant]) -> Constant:
375 return args[0] & args[1]
377 def eval_vals(self) -> ArgType:
378 rval: ArgType = super().eval_vals()
379 if 0 in self.args:
380 return 0
381 return rval
384class Or(BinaryOp):
385 @staticmethod
386 def _const_eval(args: list[Constant]) -> Constant:
387 return args[0] | args[1]
390class Xor(BinaryOp):
391 @staticmethod
392 def _const_eval(args: list[Constant]) -> Constant:
393 return args[0] ^ args[1]
396class BitAnd(And, BitLogicExp):
397 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
398 self.op = BitWiseOp.AND
399 self.args = [arg1, arg2]
402class BitOr(Or, BitLogicExp):
403 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
404 self.op = BitWiseOp.OR
405 self.args = [arg1, arg2]
407 def eval_vals(self) -> ArgType:
408 rval: ArgType = super().eval_vals()
409 if 1 in self.args: 409 ↛ 411line 409 didn't jump to line 411 because the condition on line 409 was always true
410 return 1
411 return rval
414class BitXor(Xor, BitLogicExp):
415 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
416 self.op = BitWiseOp.XOR
417 self.args = [arg1, arg2]
420class BitNot(UnaryOp, BitLogicExp):
421 def __init__(self, arg1: BitArgType) -> None:
422 self.op = BitWiseOp.NOT
423 self.args = [arg1]
425 @staticmethod
426 def _const_eval(args: list[Constant]) -> Constant:
427 return 1 - args[0]
430class BitZero(NullaryOp, BitLogicExp):
431 def __init__(self) -> None:
432 self.op = BitWiseOp.ZERO
433 self.args = []
435 @staticmethod
436 def _const_eval(args: list[Constant]) -> Constant:
437 return 0
440class BitOne(NullaryOp, BitLogicExp):
441 def __init__(self) -> None:
442 self.op = BitWiseOp.ONE
443 self.args = []
445 @staticmethod
446 def _const_eval(args: list[Constant]) -> Constant:
447 return 1
450class RegAnd(And, RegLogicExp):
451 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
452 self.op = RegWiseOp.AND
453 self.args = [arg1, arg2]
456class RegOr(Or, RegLogicExp):
457 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
458 self.op = RegWiseOp.OR
459 self.args = [arg1, arg2]
462class RegXor(Xor, RegLogicExp):
463 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
464 self.op = RegWiseOp.XOR
465 self.args = [arg1, arg2]
468class RegAdd(BinaryOp, RegLogicExp):
469 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
470 self.op = RegWiseOp.ADD
471 self.args = [arg1, arg2]
474class RegSub(BinaryOp, RegLogicExp):
475 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
476 self.op = RegWiseOp.SUB
477 self.args = [arg1, arg2]
480class RegMul(BinaryOp, RegLogicExp):
481 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
482 self.op = RegWiseOp.MUL
483 self.args = [arg1, arg2]
486class RegDiv(BinaryOp, RegLogicExp):
487 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
488 self.op = RegWiseOp.DIV
489 self.args = [arg1, arg2]
492class RegPow(BinaryOp, RegLogicExp):
493 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
494 self.op = RegWiseOp.POW
495 self.args = [arg1, arg2]
498class RegLsh(BinaryOp, RegLogicExp):
499 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
500 self.op = RegWiseOp.LSH
501 self.args = [arg1, arg2]
504class RegNeg(UnaryOp, RegLogicExp):
505 def __init__(self, arg1: RegArgType) -> None:
506 self.op = RegWiseOp.NEG
507 self.args = [arg1]
510class RegNot(UnaryOp, RegLogicExp):
511 def __init__(self, arg1: RegArgType) -> None:
512 self.op = RegWiseOp.NOT
513 self.args = [arg1]
516class RegRsh(BinaryOp, RegLogicExp):
517 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
518 self.op = RegWiseOp.RSH
519 self.args = [arg1, arg2]
522class PredicateExp(BinaryOp):
523 """
524 A binary predicate where the arguments are either
525 Bits, BitRegisters, or Constants.
526 """
529class Eq(PredicateExp):
530 @staticmethod
531 def _const_eval(args: list[Constant]) -> Constant:
532 return args[0] == args[1]
535class Neq(PredicateExp):
536 @staticmethod
537 def _const_eval(args: list[Constant]) -> Constant:
538 return 1 - Eq._const_eval(args) # noqa: SLF001
541class BitEq(Eq, BitLogicExp):
542 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
543 self.op = BitWiseOp.EQ
544 self.args = [arg1, arg2]
547class BitNeq(Neq, BitLogicExp):
548 def __init__(self, arg1: BitArgType, arg2: BitArgType) -> None:
549 self.op = BitWiseOp.NEQ
550 self.args = [arg1, arg2]
553class RegEq(Eq, RegLogicExp):
554 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
555 self.op = RegWiseOp.EQ
556 self.args = [arg1, arg2]
559class RegNeq(Neq, RegLogicExp):
560 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
561 self.op = RegWiseOp.NEQ
562 self.args = [arg1, arg2]
565class RegLt(PredicateExp, RegLogicExp):
566 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
567 self.op = RegWiseOp.LT
568 self.args = [arg1, arg2]
570 @staticmethod
571 def _const_eval(args: list[Constant]) -> Constant:
572 return args[0] < args[1]
575class RegGt(PredicateExp, RegLogicExp):
576 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
577 self.op = RegWiseOp.GT
578 self.args = [arg1, arg2]
580 @staticmethod
581 def _const_eval(args: list[Constant]) -> Constant:
582 return args[0] > args[1]
585class RegLeq(PredicateExp, RegLogicExp):
586 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
587 self.op = RegWiseOp.LEQ
588 self.args = [arg1, arg2]
590 @staticmethod
591 def _const_eval(args: list[Constant]) -> Constant:
592 return args[0] <= args[1]
595class RegGeq(PredicateExp, RegLogicExp):
596 def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None:
597 self.op = RegWiseOp.GEQ
598 self.args = [arg1, arg2]
600 @staticmethod
601 def _const_eval(args: list[Constant]) -> Constant:
602 return args[0] >= args[1]
605def reg_eq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp:
606 """Function to express a BitRegister equality predicate, i.e.
607 for a register ``r``, ``(r == 5)`` is expressed as ``reg_eq(r, 5)``"""
608 return RegEq(register, value)
611def reg_neq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp:
612 """Function to express a BitRegister inequality predicate, i.e.
613 for a register ``r``, ``(r != 5)`` is expressed as ``reg_neq(r, 5)``"""
614 return RegNeq(register, value)
617def reg_lt(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp:
618 """Function to express a BitRegister less than predicate, i.e.
619 for a register ``r``, ``(r < 5)`` is expressed as ``reg_lt(r, 5)``"""
620 return RegLt(register, value)
623def reg_gt(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp:
624 """Function to express a BitRegister greater than predicate, i.e.
625 for a register ``r``, ``(r > 5)`` is expressed as ``reg_gt(r, 5)``"""
626 return RegGt(register, value)
629def reg_leq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp:
630 """Function to express a BitRegister less than or equal to predicate,
631 i.e. for a register ``r``, ``(r <= 5)`` is expressed as ``reg_leq(r, 5)``"""
632 return RegLeq(register, value)
635def reg_geq(register: RegLogicExp | BitRegister, value: Constant) -> RegLogicExp:
636 """Function to express a BitRegister greater than or equal to
637 predicate, i.e. for a register ``r``, ``(r >= 5)`` is expressed as
638 ``reg_geq(r, 5)``"""
639 return RegGeq(register, value)
642def if_bit(bit: Bit | BitLogicExp) -> PredicateExp:
643 """Equivalent of ``if bit:``."""
644 return BitEq(bit, 1)
647def if_not_bit(bit: Bit | BitLogicExp) -> PredicateExp:
648 """Equivalent of ``if not bit:``."""
649 return BitEq(bit, 0)
652def create_bit_logic_exp( # noqa: PLR0911
653 op: BitWiseOp, args: Sequence[BitArgType]
654) -> BitLogicExp:
655 """
656 Builds the :py:class:`LogicExp` corresponding to applying the given
657 :py:class:`BitWiseOp` to some sequence of bits.
658 """
659 match op:
660 case BitWiseOp.AND:
661 assert len(args) == 2 # noqa: PLR2004
662 return BitAnd(args[0], args[1])
663 case BitWiseOp.OR:
664 assert len(args) == 2 # noqa: PLR2004
665 return BitOr(args[0], args[1])
666 case BitWiseOp.XOR:
667 assert len(args) == 2 # noqa: PLR2004
668 return BitXor(args[0], args[1])
669 case BitWiseOp.NOT:
670 assert len(args) == 1
671 return BitNot(args[0])
672 case BitWiseOp.EQ:
673 assert len(args) == 2 # noqa: PLR2004
674 return BitEq(args[0], args[1])
675 case BitWiseOp.NEQ:
676 assert len(args) == 2 # noqa: PLR2004
677 return BitNeq(args[0], args[1])
678 case BitWiseOp.ZERO:
679 assert len(args) == 0
680 return BitZero()
681 case BitWiseOp.ONE: 681 ↛ exitline 681 didn't return from function 'create_bit_logic_exp' because the pattern on line 681 always matched
682 assert len(args) == 0
683 return BitOne()
686def create_reg_logic_exp( # noqa: PLR0911, PLR0912
687 op: RegWiseOp, args: Sequence[RegArgType]
688) -> RegLogicExp:
689 """
690 Builds the :py:class:`LogicExp` corresponding to applying the given
691 :py:class:`RegWiseOp` to some sequence of registers.
692 """
693 if op == RegWiseOp.AND:
694 assert len(args) == 2 # noqa: PLR2004
695 return RegAnd(args[0], args[1])
696 if op == RegWiseOp.OR:
697 assert len(args) == 2 # noqa: PLR2004
698 return RegOr(args[0], args[1])
699 if op == RegWiseOp.XOR:
700 assert len(args) == 2 # noqa: PLR2004
701 return RegXor(args[0], args[1])
702 if op == RegWiseOp.ADD:
703 assert len(args) == 2 # noqa: PLR2004
704 return RegAdd(args[0], args[1])
705 if op == RegWiseOp.SUB:
706 if len(args) == 2: # noqa: PLR2004 706 ↛ 708line 706 didn't jump to line 708 because the condition on line 706 was always true
707 return RegSub(args[0], args[1])
708 if len(args) == 1:
709 return RegNeg(args[0])
710 if op == RegWiseOp.NEG: 710 ↛ 711line 710 didn't jump to line 711 because the condition on line 710 was never true
711 assert len(args) == 1
712 return RegNeg(args[0])
713 if op == RegWiseOp.MUL:
714 assert len(args) == 2 # noqa: PLR2004
715 return RegMul(args[0], args[1])
716 if op == RegWiseOp.DIV:
717 assert len(args) == 2 # noqa: PLR2004
718 return RegDiv(args[0], args[1])
719 if op == RegWiseOp.POW:
720 assert len(args) == 2 # noqa: PLR2004
721 return RegPow(args[0], args[1])
722 if op == RegWiseOp.LSH:
723 assert len(args) == 2 # noqa: PLR2004
724 return RegLsh(args[0], args[1])
725 if op == RegWiseOp.RSH:
726 assert len(args) == 2 # noqa: PLR2004
727 return RegRsh(args[0], args[1])
728 if op == RegWiseOp.EQ:
729 assert len(args) == 2 # noqa: PLR2004
730 return RegEq(args[0], args[1])
731 if op == RegWiseOp.NEQ:
732 assert len(args) == 2 # noqa: PLR2004
733 return RegNeq(args[0], args[1])
734 if op == RegWiseOp.LT:
735 assert len(args) == 2 # noqa: PLR2004
736 return RegLt(args[0], args[1])
737 if op == RegWiseOp.GT:
738 assert len(args) == 2 # noqa: PLR2004
739 return RegGt(args[0], args[1])
740 if op == RegWiseOp.LEQ:
741 assert len(args) == 2 # noqa: PLR2004
742 return RegLeq(args[0], args[1])
743 if op == RegWiseOp.GEQ:
744 assert len(args) == 2 # noqa: PLR2004
745 return RegGeq(args[0], args[1])
746 if op == RegWiseOp.NOT: 746 ↛ 749line 746 didn't jump to line 749 because the condition on line 746 was always true
747 assert len(args) == 1
748 return RegNot(args[0])
749 raise ValueError("op type not supported")
752def create_logic_exp(op: Ops, args: Sequence[ArgType]) -> LogicExp:
753 """
754 Builds the :py:class:`LogicExp` corresponding to applying the given
755 :py:class:`BitWiseOp` or :py:class:`RegWiseOp` to some sequence of
756 arguments.
757 """
758 if isinstance(op, BitWiseOp):
759 bit_args = []
760 args = [
761 arg[0] if isinstance(arg, BitRegister) and arg.size == 1 else arg
762 for arg in args
763 ]
764 for arg in args:
765 assert isinstance(arg, BitLogicExp | Bit | Constant)
766 bit_args.append(arg)
767 return create_bit_logic_exp(op, bit_args)
768 assert isinstance(op, RegWiseOp)
769 reg_args = []
770 for arg in args:
771 assert isinstance(arg, RegLogicExp | BitRegister | Constant)
772 reg_args.append(arg)
773 return create_reg_logic_exp(op, reg_args)
776def create_predicate_exp( # noqa: PLR0911
777 op: Ops, args: Sequence[ArgType]
778) -> PredicateExp:
779 """
780 Builds the :py:class:`LogicExp` corresponding to applying a given
781 comparison predicate to some sequence of arguments.
782 """
783 if op == BitWiseOp.EQ:
784 assert len(args) == 2 # noqa: PLR2004
785 assert isinstance(args[0], BitLogicExp | Bit | int)
786 assert isinstance(args[1], BitLogicExp | Bit | int)
787 return BitEq(args[0], args[1])
788 if op == BitWiseOp.NEQ:
789 assert len(args) == 2 # noqa: PLR2004
790 assert isinstance(args[0], BitLogicExp | Bit | int)
791 assert isinstance(args[1], BitLogicExp | Bit | int)
792 return BitNeq(args[0], args[1])
793 if op == RegWiseOp.EQ:
794 assert len(args) == 2 # noqa: PLR2004
795 assert isinstance(args[0], RegLogicExp | BitRegister | int)
796 assert isinstance(args[1], RegLogicExp | BitRegister | int)
797 return RegEq(args[0], args[1])
798 if op == RegWiseOp.NEQ:
799 assert len(args) == 2 # noqa: PLR2004
800 assert isinstance(args[0], RegLogicExp | BitRegister | int)
801 assert isinstance(args[1], RegLogicExp | BitRegister | int)
802 return RegNeq(args[0], args[1])
803 if op == RegWiseOp.LT:
804 assert len(args) == 2 # noqa: PLR2004
805 assert isinstance(args[0], RegLogicExp | BitRegister | int)
806 assert isinstance(args[1], RegLogicExp | BitRegister | int)
807 return RegLt(args[0], args[1])
808 if op == RegWiseOp.GT:
809 assert len(args) == 2 # noqa: PLR2004
810 assert isinstance(args[0], RegLogicExp | BitRegister | int)
811 assert isinstance(args[1], RegLogicExp | BitRegister | int)
812 return RegGt(args[0], args[1])
813 if op == RegWiseOp.LEQ:
814 assert len(args) == 2 # noqa: PLR2004
815 assert isinstance(args[0], RegLogicExp | BitRegister | int)
816 assert isinstance(args[1], RegLogicExp | BitRegister | int)
817 return RegLeq(args[0], args[1])
818 if op == RegWiseOp.GEQ: 818 ↛ 823line 818 didn't jump to line 823 because the condition on line 818 was always true
819 assert len(args) == 2 # noqa: PLR2004
820 assert isinstance(args[0], RegLogicExp | BitRegister | int)
821 assert isinstance(args[1], RegLogicExp | BitRegister | int)
822 return RegGeq(args[0], args[1])
823 raise ValueError("op type not supported")