PyDecisionGraph 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of PyDecisionGraph might be problematic. Click here for more details.
- decision_tree/__init__.py +36 -0
- decision_tree/abc.py +1147 -0
- decision_tree/collection.py +93 -0
- decision_tree/exc.py +38 -0
- decision_tree/expression.py +478 -0
- decision_tree/logic_group.py +307 -0
- decision_tree/node.py +180 -0
- pydecisiongraph-0.1.0.dist-info/LICENSE +373 -0
- pydecisiongraph-0.1.0.dist-info/METADATA +21 -0
- pydecisiongraph-0.1.0.dist-info/RECORD +12 -0
- pydecisiongraph-0.1.0.dist-info/WHEEL +5 -0
- pydecisiongraph-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import Literal, Any, Self
|
|
6
|
+
|
|
7
|
+
from . import AttrExpression, LogicMapping
|
|
8
|
+
from .abc import LogicGroup, SkipContextsBlock
|
|
9
|
+
|
|
10
|
+
__all__ = ['SignalLogicGroup', 'InstantConfirmationLogicGroup', 'RequestAction', 'PendingRequest', 'DelayedConfirmationLogicGroup', 'RacingConfirmationLogicGroup', 'BarrierConfirmationLogicGroup']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SignalLogicGroup(LogicGroup):
|
|
14
|
+
def __init__(self, name: str, parent: Self = None, contexts: dict[str, Any] = None):
|
|
15
|
+
super().__init__(name=name, parent=parent, contexts=contexts)
|
|
16
|
+
|
|
17
|
+
def get(self, attr: str, dtype: type = None, repr: str = None):
|
|
18
|
+
"""
|
|
19
|
+
Retrieve an attribute as a LogicExpression.
|
|
20
|
+
"""
|
|
21
|
+
return AttrExpression(attr=attr, logic_group=self, dtype=dtype, repr=repr)
|
|
22
|
+
|
|
23
|
+
def reset(self):
|
|
24
|
+
self.signal = 0
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def signal(self):
|
|
28
|
+
return self.contexts.get('signal', 0)
|
|
29
|
+
|
|
30
|
+
@signal.setter
|
|
31
|
+
def signal(self, value: int):
|
|
32
|
+
self.contexts['signal'] = value
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class InstantConfirmationLogicGroup(SignalLogicGroup):
|
|
36
|
+
def __init__(self, parent: SignalLogicGroup, name: str = None):
|
|
37
|
+
super().__init__(
|
|
38
|
+
name=f'{parent.name}.Instant' if name is None else name,
|
|
39
|
+
parent=parent
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def reset(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def confirm(self, sig: Literal[-1, 1]):
|
|
46
|
+
self.signal = sig
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def signal(self):
|
|
51
|
+
return self.parent.signal
|
|
52
|
+
|
|
53
|
+
@signal.setter
|
|
54
|
+
def signal(self, value: int):
|
|
55
|
+
self.parent.signal = value
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RequestAction(enum.StrEnum):
|
|
59
|
+
open = enum.auto()
|
|
60
|
+
unwind = enum.auto()
|
|
61
|
+
idle = enum.auto()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class PendingRequest(dict):
|
|
65
|
+
class Skip(Exception):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
name: str | RequestAction,
|
|
71
|
+
timestamp: float,
|
|
72
|
+
sig: Literal[-1, 1] | int,
|
|
73
|
+
action: str,
|
|
74
|
+
timeout: float,
|
|
75
|
+
logic_group: LogicGroup = None,
|
|
76
|
+
uid: uuid.UUID = None,
|
|
77
|
+
**kwargs
|
|
78
|
+
):
|
|
79
|
+
super().__init__(
|
|
80
|
+
name=name,
|
|
81
|
+
timestamp=timestamp,
|
|
82
|
+
sig=sig,
|
|
83
|
+
timeout=timeout,
|
|
84
|
+
action=RequestAction(action),
|
|
85
|
+
uid=uuid.uuid4() if uid is None else uid,
|
|
86
|
+
**kwargs
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
self.logic_group = logic_group
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def empty(cls) -> PendingRequest:
|
|
93
|
+
return PendingRequest(
|
|
94
|
+
name='DummyRequest',
|
|
95
|
+
timestamp=0,
|
|
96
|
+
sig=0,
|
|
97
|
+
action=RequestAction.idle,
|
|
98
|
+
timeout=0,
|
|
99
|
+
uid=uuid.UUID(int=0)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def __bool__(self):
|
|
103
|
+
if not self.sig:
|
|
104
|
+
return False
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def name(self) -> str:
|
|
109
|
+
return self['name']
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def timestamp(self) -> float:
|
|
113
|
+
return self['timestamp']
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def sig(self) -> int:
|
|
117
|
+
return self['sig']
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def timeout(self) -> float:
|
|
121
|
+
return self['timeout']
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def action(self) -> RequestAction:
|
|
125
|
+
return self['action']
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def uid(self) -> uuid.UUID:
|
|
129
|
+
return self['uid']
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class DelayedConfirmationLogicGroup(SignalLogicGroup):
|
|
133
|
+
def __init__(self, parent: SignalLogicGroup, name: str = None):
|
|
134
|
+
super().__init__(
|
|
135
|
+
name=f'{parent.name}.Delayed' if name is None else name,
|
|
136
|
+
parent=parent
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def register(self, name: str, timestamp: float, sig: Literal[1, -1], timeout: float | None, action: Literal['open', 'unwind'] = 'open', uid: uuid.UUID = None, **kwargs):
|
|
140
|
+
req = self.pending_request = PendingRequest(
|
|
141
|
+
name=name,
|
|
142
|
+
logic_group=self,
|
|
143
|
+
timestamp=timestamp,
|
|
144
|
+
sig=sig,
|
|
145
|
+
timeout=timeout,
|
|
146
|
+
action=action,
|
|
147
|
+
uid=uid,
|
|
148
|
+
**kwargs
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return req
|
|
152
|
+
|
|
153
|
+
def confirm(self):
|
|
154
|
+
req = self.contexts.get('pending_request')
|
|
155
|
+
sig = 0 if req is None else req.sig
|
|
156
|
+
self.reset()
|
|
157
|
+
self.signal = sig
|
|
158
|
+
return sig
|
|
159
|
+
|
|
160
|
+
def deny(self):
|
|
161
|
+
# denying all the pending request
|
|
162
|
+
self.reset()
|
|
163
|
+
return 0
|
|
164
|
+
|
|
165
|
+
def reset(self):
|
|
166
|
+
# self.pending_request = PendingRequest.empty()
|
|
167
|
+
self.contexts.pop('pending_request', None)
|
|
168
|
+
super().reset()
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def action(self) -> RequestAction:
|
|
172
|
+
if 'pending_request' in self.contexts:
|
|
173
|
+
return self.pending_request.action
|
|
174
|
+
|
|
175
|
+
return RequestAction.idle
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def pending_request(self) -> LogicMapping | SkipContextsBlock:
|
|
179
|
+
|
|
180
|
+
if (req := self.contexts.get('pending_request')) is None:
|
|
181
|
+
return SkipContextsBlock(True)
|
|
182
|
+
|
|
183
|
+
m = LogicMapping(
|
|
184
|
+
data=req,
|
|
185
|
+
name=f'{self.name}.PendingRequest.{req.uid.hex}',
|
|
186
|
+
logic_group=self
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return m
|
|
190
|
+
|
|
191
|
+
@pending_request.setter
|
|
192
|
+
def pending_request(self, value: PendingRequest):
|
|
193
|
+
assert isinstance(value, PendingRequest)
|
|
194
|
+
self.contexts['pending_request'] = value
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def signal(self):
|
|
198
|
+
return self.parent.signal
|
|
199
|
+
|
|
200
|
+
@signal.setter
|
|
201
|
+
def signal(self, value: Literal[-1, 0, 1]):
|
|
202
|
+
assert isinstance(value, (int, float))
|
|
203
|
+
self.parent.signal = value
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class RacingConfirmationLogicGroup(DelayedConfirmationLogicGroup):
|
|
207
|
+
|
|
208
|
+
def __init__(self, parent: SignalLogicGroup, name: str = None):
|
|
209
|
+
super().__init__(
|
|
210
|
+
name=f'{parent.name}.Racing' if name is None else name,
|
|
211
|
+
parent=parent
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def __getitem__(self, uid: uuid.UUID | str | bytes | int) -> PendingRequest:
|
|
215
|
+
if not (request_pool := self.pending_request):
|
|
216
|
+
raise KeyError(f'uid {uid} not found!')
|
|
217
|
+
|
|
218
|
+
match uid:
|
|
219
|
+
case uuid.UUID():
|
|
220
|
+
for _pending_request in request_pool:
|
|
221
|
+
if _pending_request.uid == uid:
|
|
222
|
+
return _pending_request
|
|
223
|
+
raise KeyError(f'uid {uid} not found!')
|
|
224
|
+
case str():
|
|
225
|
+
for _pending_request in request_pool:
|
|
226
|
+
if _pending_request.uid.hex == uid:
|
|
227
|
+
return _pending_request
|
|
228
|
+
raise KeyError(f'uid {uid} not found!')
|
|
229
|
+
case bytes():
|
|
230
|
+
for _pending_request in request_pool:
|
|
231
|
+
if _pending_request.uid.bytes == uid:
|
|
232
|
+
return _pending_request
|
|
233
|
+
raise KeyError(f'uid {uid} not found!')
|
|
234
|
+
case int():
|
|
235
|
+
return request_pool[uid]
|
|
236
|
+
case _:
|
|
237
|
+
raise TypeError(f'Invalid uid {uid}! Expected UUID or bytes or str!')
|
|
238
|
+
|
|
239
|
+
def register(self, name: str, timestamp: float, sig: Literal[1, -1], timeout: float, action: Literal['open', 'unwind'] = 'open', uid: uuid.UUID = None, **kwargs):
|
|
240
|
+
self.pending_request.append(
|
|
241
|
+
PendingRequest(
|
|
242
|
+
name=name,
|
|
243
|
+
timestamp=timestamp,
|
|
244
|
+
sig=sig,
|
|
245
|
+
timeout=timeout,
|
|
246
|
+
action=action,
|
|
247
|
+
uid=uid,
|
|
248
|
+
**kwargs
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def confirm(self, pending_request: PendingRequest = None, uid: uuid.UUID = None):
|
|
253
|
+
if pending_request is None and uid is None:
|
|
254
|
+
assert len(self.pending_request) == 1, ValueError('Multiple pending requests found! Must assign uid or pending_request instance!')
|
|
255
|
+
pending_request = self.pending_request[0]
|
|
256
|
+
elif pending_request is None:
|
|
257
|
+
pending_request = self.__getitem__(uid=uid)
|
|
258
|
+
|
|
259
|
+
sig = pending_request.sig
|
|
260
|
+
self.reset()
|
|
261
|
+
self.signal = sig
|
|
262
|
+
return sig
|
|
263
|
+
|
|
264
|
+
def deny(self, pending_request: PendingRequest = None, uid: uuid.UUID = None):
|
|
265
|
+
# denying all the pending request
|
|
266
|
+
if pending_request is None and uid is None:
|
|
267
|
+
self.pending_request.clear()
|
|
268
|
+
self.signal = 0
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
if pending_request is not None:
|
|
272
|
+
self.pending_request.remove(pending_request)
|
|
273
|
+
self.signal = 0
|
|
274
|
+
|
|
275
|
+
if uid is not None:
|
|
276
|
+
pending_request = self.__getitem__(uid=uid)
|
|
277
|
+
self.pending_request.remove(pending_request)
|
|
278
|
+
self.signal = 0
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def pending_request(self) -> list[PendingRequest]:
|
|
282
|
+
return self.contexts.setdefault('pending_request', [])
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class BarrierConfirmationLogicGroup(RacingConfirmationLogicGroup):
|
|
286
|
+
|
|
287
|
+
def __init__(self, parent: SignalLogicGroup, name: str = None):
|
|
288
|
+
super().__init__(
|
|
289
|
+
name=f'{parent.name}.Barrier' if name is None else name,
|
|
290
|
+
parent=parent
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def confirm(self, pending_request: PendingRequest = None, uid: uuid.UUID = None):
|
|
294
|
+
if pending_request is None and uid is None:
|
|
295
|
+
assert len(self.pending_request) == 1, ValueError('Multiple pending requests found! Must assign uid or pending_request instance!')
|
|
296
|
+
pending_request = self.pending_request[0]
|
|
297
|
+
elif pending_request is None:
|
|
298
|
+
pending_request = self.__getitem__(uid=uid)
|
|
299
|
+
|
|
300
|
+
self.pending_request.remove(pending_request)
|
|
301
|
+
|
|
302
|
+
if self.pending_request:
|
|
303
|
+
return 0
|
|
304
|
+
|
|
305
|
+
sig = pending_request.sig
|
|
306
|
+
self.signal = sig
|
|
307
|
+
return sig
|
decision_tree/node.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Any, Self
|
|
6
|
+
|
|
7
|
+
from .abc import LogicGroup, LogicNode, ActionNode, LGM, NO_CONDITION
|
|
8
|
+
from .exc import TooManyChildren, TooFewChildren
|
|
9
|
+
from .expression import ContextLogicExpression as _CLE, AttrExpression as _AE, MathExpression as _ME, ComparisonExpression as _CE, LogicalExpression as _LE
|
|
10
|
+
|
|
11
|
+
__all__ = ['NoAction', 'LongAction', 'ShortAction', 'RootLogicNode', 'ContextLogicExpression', 'AttrExpression', 'MathExpression', 'ComparisonExpression', 'LogicalExpression']
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NoAction(ActionNode):
|
|
15
|
+
def __init__(self, auto_connect: bool = True):
|
|
16
|
+
super().__init__(
|
|
17
|
+
action=None,
|
|
18
|
+
repr='<NoAction>',
|
|
19
|
+
auto_connect=auto_connect
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def eval(self, enforce_dtype: bool = False) -> ActionNode:
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LongAction(ActionNode):
|
|
27
|
+
def __init__(self, sig: int = 1, auto_connect: bool = True):
|
|
28
|
+
super().__init__(
|
|
29
|
+
action=sig,
|
|
30
|
+
repr=f'<LongAction>(sig = {sig})',
|
|
31
|
+
auto_connect=auto_connect
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def eval(self, enforce_dtype: bool = False) -> ActionNode:
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ShortAction(ActionNode):
|
|
39
|
+
def __init__(self, sig: int = -1, auto_connect: bool = True):
|
|
40
|
+
super().__init__(
|
|
41
|
+
action=sig,
|
|
42
|
+
repr=f'<ShortAction>(sig = {sig})',
|
|
43
|
+
auto_connect=auto_connect
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def eval(self, enforce_dtype: bool = False) -> ActionNode:
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class RootLogicNode(LogicNode):
|
|
51
|
+
def __init__(self):
|
|
52
|
+
super().__init__(
|
|
53
|
+
expression=True,
|
|
54
|
+
repr=f'Entry Point'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def _entry_check(self):
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
def _on_enter(self):
|
|
61
|
+
self._inspection_mode = LGM.inspection_mode
|
|
62
|
+
LGM.inspection_mode = True
|
|
63
|
+
LGM._active_nodes.clear()
|
|
64
|
+
LGM.enter_expression(node=self)
|
|
65
|
+
|
|
66
|
+
def _on_exit(self):
|
|
67
|
+
LGM.exit_expression(node=self)
|
|
68
|
+
if hasattr(self, '_inspection_mode'):
|
|
69
|
+
LGM.inspection_mode = self._inspection_mode
|
|
70
|
+
else:
|
|
71
|
+
LGM.inspection_mode = False
|
|
72
|
+
|
|
73
|
+
def append(self, expression: Self, edge_condition: Any = None):
|
|
74
|
+
if self.nodes:
|
|
75
|
+
raise TooManyChildren()
|
|
76
|
+
super().append(expression=expression, edge_condition=NO_CONDITION)
|
|
77
|
+
|
|
78
|
+
def eval_recursively(self, **kwargs):
|
|
79
|
+
return self.child.eval_recursively(**kwargs)
|
|
80
|
+
|
|
81
|
+
def to_html(self, with_group=True, dry_run=True, filename="decision_tree.html", **kwargs):
|
|
82
|
+
return self.child.to_html(with_group=with_group, dry_run=dry_run, filename=filename, **kwargs)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def child(self) -> LogicNode:
|
|
86
|
+
if self.nodes:
|
|
87
|
+
return self.last_node
|
|
88
|
+
|
|
89
|
+
raise TooFewChildren()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ContextLogicExpression(_CLE, LogicNode, metaclass=ABCMeta):
|
|
93
|
+
def __init__(self, expression: float | int | bool | Exception | Callable[[], Any], dtype: type = None, repr: str = None, logic_group: LogicGroup = None):
|
|
94
|
+
super().__init__(expression=expression, dtype=dtype, repr=repr, logic_group=logic_group)
|
|
95
|
+
LogicNode.__init__(self=self, expression=expression, dtype=dtype, repr=repr)
|
|
96
|
+
|
|
97
|
+
# magic method to invoke AttrExpression
|
|
98
|
+
def __getitem__(self, key: str) -> AttrExpression:
|
|
99
|
+
return AttrExpression(attr=key, logic_group=self.logic_group)
|
|
100
|
+
|
|
101
|
+
def __getattr__(self, key: str) -> AttrExpression:
|
|
102
|
+
return AttrExpression(attr=key, logic_group=self.logic_group)
|
|
103
|
+
|
|
104
|
+
# math operation to invoke MathExpression
|
|
105
|
+
|
|
106
|
+
def __add__(self, other: int | float | bool | Self) -> Self:
|
|
107
|
+
return MathExpression(left=self, op=MathExpression.Operator.add, right=other, logic_group=self.logic_group)
|
|
108
|
+
|
|
109
|
+
def __sub__(self, other: int | float | bool | Self) -> Self:
|
|
110
|
+
return MathExpression(left=self, op=MathExpression.Operator.sub, right=other, logic_group=self.logic_group)
|
|
111
|
+
|
|
112
|
+
def __mul__(self, other: int | float | bool | Self) -> Self:
|
|
113
|
+
return MathExpression(left=self, op=MathExpression.Operator.mul, right=other, logic_group=self.logic_group)
|
|
114
|
+
|
|
115
|
+
def __truediv__(self, other: int | float | bool | Self) -> Self:
|
|
116
|
+
return MathExpression(left=self, op=MathExpression.Operator.truediv, right=other, logic_group=self.logic_group)
|
|
117
|
+
|
|
118
|
+
def __floordiv__(self, other: int | float | bool | Self) -> Self:
|
|
119
|
+
return MathExpression(left=self, op=MathExpression.Operator.floordiv, right=other, logic_group=self.logic_group)
|
|
120
|
+
|
|
121
|
+
def __pow__(self, other: int | float | bool | Self) -> Self:
|
|
122
|
+
return MathExpression(left=self, op=MathExpression.Operator.pow, right=other, logic_group=self.logic_group)
|
|
123
|
+
|
|
124
|
+
def __neg__(self):
|
|
125
|
+
return MathExpression(left=self, op=MathExpression.Operator.neg, repr=f'-{self.repr}', logic_group=self.logic_group)
|
|
126
|
+
|
|
127
|
+
# Comparison operation to invoke ComparisonExpression
|
|
128
|
+
|
|
129
|
+
def __eq__(self, other: int | float | bool | str | Self) -> Self:
|
|
130
|
+
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.eq, right=other, logic_group=self.logic_group)
|
|
131
|
+
|
|
132
|
+
def __ne__(self, other: int | float | bool | str | Self) -> Self:
|
|
133
|
+
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.ne, right=other, logic_group=self.logic_group)
|
|
134
|
+
|
|
135
|
+
def __gt__(self, other: int | float | bool | Self) -> Self:
|
|
136
|
+
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.gt, right=other, logic_group=self.logic_group)
|
|
137
|
+
|
|
138
|
+
def __ge__(self, other: int | float | bool | Self) -> Self:
|
|
139
|
+
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.ge, right=other, logic_group=self.logic_group)
|
|
140
|
+
|
|
141
|
+
def __lt__(self, other: int | float | bool | Self) -> Self:
|
|
142
|
+
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.lt, right=other, logic_group=self.logic_group)
|
|
143
|
+
|
|
144
|
+
def __le__(self, other: int | float | bool | Self) -> Self:
|
|
145
|
+
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.le, right=other, logic_group=self.logic_group)
|
|
146
|
+
|
|
147
|
+
# Logical operation to invoke LogicalExpression
|
|
148
|
+
|
|
149
|
+
def __and__(self, other: int | float | bool | Self) -> Self:
|
|
150
|
+
return LogicalExpression(left=self, op=LogicalExpression.Operator.and_, right=other, logic_group=self.logic_group)
|
|
151
|
+
|
|
152
|
+
def __or__(self, other: Self | bool) -> Self:
|
|
153
|
+
return LogicalExpression(left=self, op=LogicalExpression.Operator.or_, right=other, logic_group=self.logic_group)
|
|
154
|
+
|
|
155
|
+
def __invert__(self) -> Self:
|
|
156
|
+
return LogicalExpression(left=self, op=LogicalExpression.Operator.not_, repr=f'~{self.repr}', logic_group=self.logic_group)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class AttrExpression(_AE, ContextLogicExpression):
|
|
160
|
+
def __init__(self, attr: str | int, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
|
|
161
|
+
super().__init__(attr=attr, repr=repr, logic_group=logic_group)
|
|
162
|
+
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class MathExpression(_ME, ContextLogicExpression):
|
|
166
|
+
def __init__(self, left: ContextLogicExpression | int | float, op: _ME.Operator, right: ContextLogicExpression | int | float = None, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
|
|
167
|
+
super().__init__(left=left, op=op, right=right, dtype=dtype, repr=repr, logic_group=logic_group)
|
|
168
|
+
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ComparisonExpression(_CE, ContextLogicExpression):
|
|
172
|
+
def __init__(self, left: ContextLogicExpression | int | float, op: _CE.Operator, right: ContextLogicExpression | int | float = None, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
|
|
173
|
+
super().__init__(left=left, op=op, right=right, dtype=dtype, repr=repr, logic_group=logic_group)
|
|
174
|
+
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class LogicalExpression(_LE, ContextLogicExpression):
|
|
178
|
+
def __init__(self, left: ContextLogicExpression | int | float, op: _LE.Operator, right: ContextLogicExpression | int | float = None, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
|
|
179
|
+
super().__init__(left=left, op=op, right=right, dtype=dtype, repr=repr, logic_group=logic_group)
|
|
180
|
+
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
|