egglog 11.2.0__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 11.3.0__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.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 egglog might be problematic. Click here for more details.
- egglog/bindings.cpython-313-aarch64-linux-gnu.so +0 -0
- egglog/builtins.py +4 -0
- egglog/declarations.py +19 -1
- egglog/egraph.py +42 -5
- egglog/egraph_state.py +129 -6
- egglog/examples/jointree.py +0 -3
- egglog/pretty.py +32 -7
- egglog/runtime.py +11 -2
- {egglog-11.2.0.dist-info → egglog-11.3.0.dist-info}/METADATA +19 -1
- {egglog-11.2.0.dist-info → egglog-11.3.0.dist-info}/RECORD +12 -12
- {egglog-11.2.0.dist-info → egglog-11.3.0.dist-info}/WHEEL +0 -0
- {egglog-11.2.0.dist-info → egglog-11.3.0.dist-info}/licenses/LICENSE +0 -0
|
Binary file
|
egglog/builtins.py
CHANGED
|
@@ -103,6 +103,10 @@ class String(BuiltinExpr):
|
|
|
103
103
|
@method(egg_fn="replace")
|
|
104
104
|
def replace(self, old: StringLike, new: StringLike) -> String: ...
|
|
105
105
|
|
|
106
|
+
@method(preserve=True)
|
|
107
|
+
def __add__(self, other: StringLike) -> String:
|
|
108
|
+
return join(self, other)
|
|
109
|
+
|
|
106
110
|
|
|
107
111
|
StringLike: TypeAlias = String | str
|
|
108
112
|
|
egglog/declarations.py
CHANGED
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
10
|
from functools import cached_property
|
|
11
11
|
from typing import TYPE_CHECKING, ClassVar, Literal, Protocol, TypeAlias, TypeVar, Union, cast, runtime_checkable
|
|
12
|
+
from uuid import UUID
|
|
12
13
|
from weakref import WeakValueDictionary
|
|
13
14
|
|
|
14
15
|
from typing_extensions import Self, assert_never
|
|
@@ -20,6 +21,7 @@ if TYPE_CHECKING:
|
|
|
20
21
|
__all__ = [
|
|
21
22
|
"ActionCommandDecl",
|
|
22
23
|
"ActionDecl",
|
|
24
|
+
"BackOffDecl",
|
|
23
25
|
"BiRewriteDecl",
|
|
24
26
|
"CallDecl",
|
|
25
27
|
"CallableDecl",
|
|
@@ -52,6 +54,7 @@ __all__ = [
|
|
|
52
54
|
"JustTypeRef",
|
|
53
55
|
"LetDecl",
|
|
54
56
|
"LetRefDecl",
|
|
57
|
+
"LetSchedulerDecl",
|
|
55
58
|
"LitDecl",
|
|
56
59
|
"LitType",
|
|
57
60
|
"MethodRef",
|
|
@@ -790,9 +793,24 @@ class SequenceDecl:
|
|
|
790
793
|
class RunDecl:
|
|
791
794
|
ruleset: str
|
|
792
795
|
until: tuple[FactDecl, ...] | None
|
|
796
|
+
scheduler: BackOffDecl | None = None
|
|
793
797
|
|
|
794
798
|
|
|
795
|
-
|
|
799
|
+
@dataclass(frozen=True)
|
|
800
|
+
class LetSchedulerDecl:
|
|
801
|
+
scheduler: BackOffDecl
|
|
802
|
+
inner: ScheduleDecl
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
ScheduleDecl: TypeAlias = SaturateDecl | RepeatDecl | SequenceDecl | RunDecl | LetSchedulerDecl
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
@dataclass(frozen=True)
|
|
809
|
+
class BackOffDecl:
|
|
810
|
+
id: UUID
|
|
811
|
+
match_limit: int | None
|
|
812
|
+
ban_length: int | None
|
|
813
|
+
|
|
796
814
|
|
|
797
815
|
##
|
|
798
816
|
# Facts
|
egglog/egraph.py
CHANGED
|
@@ -23,6 +23,7 @@ from typing import (
|
|
|
23
23
|
get_type_hints,
|
|
24
24
|
overload,
|
|
25
25
|
)
|
|
26
|
+
from uuid import uuid4
|
|
26
27
|
from warnings import warn
|
|
27
28
|
|
|
28
29
|
import graphviz
|
|
@@ -45,6 +46,7 @@ if TYPE_CHECKING:
|
|
|
45
46
|
|
|
46
47
|
__all__ = [
|
|
47
48
|
"Action",
|
|
49
|
+
"BackOff",
|
|
48
50
|
"BaseExpr",
|
|
49
51
|
"BuiltinExpr",
|
|
50
52
|
"Command",
|
|
@@ -63,6 +65,7 @@ __all__ = [
|
|
|
63
65
|
"_RewriteBuilder",
|
|
64
66
|
"_SetBuilder",
|
|
65
67
|
"_UnionBuilder",
|
|
68
|
+
"back_off",
|
|
66
69
|
"birewrite",
|
|
67
70
|
"check",
|
|
68
71
|
"check_eq",
|
|
@@ -905,8 +908,8 @@ class EGraph:
|
|
|
905
908
|
|
|
906
909
|
def _run_schedule(self, schedule: Schedule) -> bindings.RunReport:
|
|
907
910
|
self._add_decls(schedule)
|
|
908
|
-
|
|
909
|
-
(command_output,) = self._egraph.run_program(
|
|
911
|
+
cmd = self._state.run_schedule_to_egg(schedule.schedule)
|
|
912
|
+
(command_output,) = self._egraph.run_program(cmd)
|
|
910
913
|
assert isinstance(command_output, bindings.RunScheduleOutput)
|
|
911
914
|
return command_output.report
|
|
912
915
|
|
|
@@ -1033,7 +1036,7 @@ class EGraph:
|
|
|
1033
1036
|
split_primitive_outputs = kwargs.pop("split_primitive_outputs", True)
|
|
1034
1037
|
split_functions = kwargs.pop("split_functions", [])
|
|
1035
1038
|
include_temporary_functions = kwargs.pop("include_temporary_functions", False)
|
|
1036
|
-
n_inline_leaves = kwargs.pop("n_inline_leaves",
|
|
1039
|
+
n_inline_leaves = kwargs.pop("n_inline_leaves", 0)
|
|
1037
1040
|
serialized = self._egraph.serialize(
|
|
1038
1041
|
[],
|
|
1039
1042
|
max_functions=max_functions,
|
|
@@ -1786,17 +1789,51 @@ def to_runtime_expr(expr: BaseExpr) -> RuntimeExpr:
|
|
|
1786
1789
|
return expr
|
|
1787
1790
|
|
|
1788
1791
|
|
|
1789
|
-
def run(ruleset: Ruleset | None = None, *until: FactLike) -> Schedule:
|
|
1792
|
+
def run(ruleset: Ruleset | None = None, *until: FactLike, scheduler: BackOff | None = None) -> Schedule:
|
|
1790
1793
|
"""
|
|
1791
1794
|
Create a run configuration.
|
|
1792
1795
|
"""
|
|
1793
1796
|
facts = _fact_likes(until)
|
|
1794
1797
|
return Schedule(
|
|
1795
1798
|
Thunk.fn(Declarations.create, ruleset, *facts),
|
|
1796
|
-
RunDecl(
|
|
1799
|
+
RunDecl(
|
|
1800
|
+
ruleset.__egg_name__ if ruleset else "",
|
|
1801
|
+
tuple(f.fact for f in facts) or None,
|
|
1802
|
+
scheduler.scheduler if scheduler else None,
|
|
1803
|
+
),
|
|
1797
1804
|
)
|
|
1798
1805
|
|
|
1799
1806
|
|
|
1807
|
+
def back_off(match_limit: None | int = None, ban_length: None | int = None) -> BackOff:
|
|
1808
|
+
"""
|
|
1809
|
+
Create a backoff scheduler configuration.
|
|
1810
|
+
|
|
1811
|
+
```python
|
|
1812
|
+
schedule = run(analysis_ruleset).saturate() + run(ruleset, scheduler=back_off(match_limit=1000, ban_length=5)) * 10
|
|
1813
|
+
```
|
|
1814
|
+
This will run the `analysis_ruleset` until saturation, then run `ruleset` 10 times, using a backoff scheduler.
|
|
1815
|
+
"""
|
|
1816
|
+
return BackOff(BackOffDecl(id=uuid4(), match_limit=match_limit, ban_length=ban_length))
|
|
1817
|
+
|
|
1818
|
+
|
|
1819
|
+
@dataclass(frozen=True)
|
|
1820
|
+
class BackOff:
|
|
1821
|
+
scheduler: BackOffDecl
|
|
1822
|
+
|
|
1823
|
+
def scope(self, schedule: Schedule) -> Schedule:
|
|
1824
|
+
"""
|
|
1825
|
+
Defines the scheduler to be created directly before the inner schedule, instead of the default which is at the
|
|
1826
|
+
most outer scope.
|
|
1827
|
+
"""
|
|
1828
|
+
return Schedule(schedule.__egg_decls_thunk__, LetSchedulerDecl(self.scheduler, schedule.schedule))
|
|
1829
|
+
|
|
1830
|
+
def __str__(self) -> str:
|
|
1831
|
+
return pretty_decl(Declarations(), self.scheduler)
|
|
1832
|
+
|
|
1833
|
+
def __repr__(self) -> str:
|
|
1834
|
+
return str(self)
|
|
1835
|
+
|
|
1836
|
+
|
|
1800
1837
|
def seq(*schedules: Schedule) -> Schedule:
|
|
1801
1838
|
"""
|
|
1802
1839
|
Run a sequence of schedules.
|
egglog/egraph_state.py
CHANGED
|
@@ -8,6 +8,7 @@ import re
|
|
|
8
8
|
from collections import defaultdict
|
|
9
9
|
from dataclasses import dataclass, field, replace
|
|
10
10
|
from typing import TYPE_CHECKING, Literal, overload
|
|
11
|
+
from uuid import UUID
|
|
11
12
|
|
|
12
13
|
from typing_extensions import assert_never
|
|
13
14
|
|
|
@@ -89,18 +90,140 @@ class EGraphState:
|
|
|
89
90
|
cost_callables=self.cost_callables.copy(),
|
|
90
91
|
)
|
|
91
92
|
|
|
92
|
-
def
|
|
93
|
+
def run_schedule_to_egg(self, schedule: ScheduleDecl) -> bindings._Command:
|
|
94
|
+
"""
|
|
95
|
+
Turn a run schedule into an egg command.
|
|
96
|
+
|
|
97
|
+
If there exists any custom schedulers in the schedule, it will be turned into a custom extract command otherwise
|
|
98
|
+
will be a normal run command.
|
|
99
|
+
"""
|
|
100
|
+
processed_schedule = self._process_schedule(schedule)
|
|
101
|
+
if processed_schedule is None:
|
|
102
|
+
return bindings.RunSchedule(self._schedule_to_egg(schedule))
|
|
103
|
+
top_level_schedules = self._schedule_with_scheduler_to_egg(processed_schedule, [])
|
|
104
|
+
if len(top_level_schedules) == 1:
|
|
105
|
+
schedule_expr = top_level_schedules[0]
|
|
106
|
+
else:
|
|
107
|
+
schedule_expr = bindings.Call(span(), "seq", top_level_schedules)
|
|
108
|
+
return bindings.UserDefined(span(), "run-schedule", [schedule_expr])
|
|
109
|
+
|
|
110
|
+
def _process_schedule(self, schedule: ScheduleDecl) -> ScheduleDecl | None:
|
|
111
|
+
"""
|
|
112
|
+
Processes a schedule to determine if it contains any custom schedulers.
|
|
113
|
+
|
|
114
|
+
If it does, it returns a new schedule with all the required let bindings added to the other scope.
|
|
115
|
+
If not, returns none.
|
|
116
|
+
|
|
117
|
+
Also processes all rulesets in the schedule to make sure they are registered.
|
|
118
|
+
"""
|
|
119
|
+
bound_schedulers: list[UUID] = []
|
|
120
|
+
unbound_schedulers: list[BackOffDecl] = []
|
|
121
|
+
|
|
122
|
+
def helper(s: ScheduleDecl) -> None:
|
|
123
|
+
match s:
|
|
124
|
+
case LetSchedulerDecl(scheduler, inner):
|
|
125
|
+
bound_schedulers.append(scheduler.id)
|
|
126
|
+
return helper(inner)
|
|
127
|
+
case RunDecl(ruleset_name, _, scheduler):
|
|
128
|
+
self.ruleset_to_egg(ruleset_name)
|
|
129
|
+
if scheduler and scheduler.id not in bound_schedulers:
|
|
130
|
+
unbound_schedulers.append(scheduler)
|
|
131
|
+
case SaturateDecl(inner) | RepeatDecl(inner, _):
|
|
132
|
+
return helper(inner)
|
|
133
|
+
case SequenceDecl(schedules):
|
|
134
|
+
for sc in schedules:
|
|
135
|
+
helper(sc)
|
|
136
|
+
case _:
|
|
137
|
+
assert_never(s)
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
helper(schedule)
|
|
141
|
+
if not bound_schedulers and not unbound_schedulers:
|
|
142
|
+
return None
|
|
143
|
+
for scheduler in unbound_schedulers:
|
|
144
|
+
schedule = LetSchedulerDecl(scheduler, schedule)
|
|
145
|
+
return schedule
|
|
146
|
+
|
|
147
|
+
def _schedule_to_egg(self, schedule: ScheduleDecl) -> bindings._Schedule:
|
|
148
|
+
msg = "Should never reach this, let schedulers should be handled by custom scheduler"
|
|
93
149
|
match schedule:
|
|
94
150
|
case SaturateDecl(schedule):
|
|
95
|
-
return bindings.Saturate(span(), self.
|
|
151
|
+
return bindings.Saturate(span(), self._schedule_to_egg(schedule))
|
|
96
152
|
case RepeatDecl(schedule, times):
|
|
97
|
-
return bindings.Repeat(span(), times, self.
|
|
153
|
+
return bindings.Repeat(span(), times, self._schedule_to_egg(schedule))
|
|
98
154
|
case SequenceDecl(schedules):
|
|
99
|
-
return bindings.Sequence(span(), [self.
|
|
100
|
-
case RunDecl(ruleset_name, until):
|
|
101
|
-
|
|
155
|
+
return bindings.Sequence(span(), [self._schedule_to_egg(s) for s in schedules])
|
|
156
|
+
case RunDecl(ruleset_name, until, scheduler):
|
|
157
|
+
if scheduler is not None:
|
|
158
|
+
raise ValueError(msg)
|
|
102
159
|
config = bindings.RunConfig(ruleset_name, None if not until else list(map(self.fact_to_egg, until)))
|
|
103
160
|
return bindings.Run(span(), config)
|
|
161
|
+
case LetSchedulerDecl():
|
|
162
|
+
raise ValueError(msg)
|
|
163
|
+
case _:
|
|
164
|
+
assert_never(schedule)
|
|
165
|
+
|
|
166
|
+
def _schedule_with_scheduler_to_egg( # noqa: C901, PLR0912
|
|
167
|
+
self, schedule: ScheduleDecl, bound_schedulers: list[UUID]
|
|
168
|
+
) -> list[bindings._Expr]:
|
|
169
|
+
"""
|
|
170
|
+
Turns a scheduler into an egg expression, to be used with a custom extract command.
|
|
171
|
+
|
|
172
|
+
The bound_schedulers is a list of all the schedulers that have been bound. We can lookup their name as `_scheduler_{index}`.
|
|
173
|
+
"""
|
|
174
|
+
match schedule:
|
|
175
|
+
case LetSchedulerDecl(BackOffDecl(id, match_limit, ban_length), inner):
|
|
176
|
+
name = f"_scheduler_{len(bound_schedulers)}"
|
|
177
|
+
bound_schedulers.append(id)
|
|
178
|
+
args: list[bindings._Expr] = []
|
|
179
|
+
if match_limit is not None:
|
|
180
|
+
args.append(bindings.Var(span(), ":match-limit"))
|
|
181
|
+
args.append(bindings.Lit(span(), bindings.Int(match_limit)))
|
|
182
|
+
if ban_length is not None:
|
|
183
|
+
args.append(bindings.Var(span(), ":ban-length"))
|
|
184
|
+
args.append(bindings.Lit(span(), bindings.Int(ban_length)))
|
|
185
|
+
back_off_decl = bindings.Call(span(), "back-off", args)
|
|
186
|
+
let_decl = bindings.Call(span(), "let-scheduler", [bindings.Var(span(), name), back_off_decl])
|
|
187
|
+
return [let_decl, *self._schedule_with_scheduler_to_egg(inner, bound_schedulers)]
|
|
188
|
+
case RunDecl(ruleset_name, until, scheduler):
|
|
189
|
+
args = [bindings.Var(span(), ruleset_name)]
|
|
190
|
+
if scheduler:
|
|
191
|
+
name = "run-with"
|
|
192
|
+
scheduler_name = f"_scheduler_{bound_schedulers.index(scheduler.id)}"
|
|
193
|
+
args.insert(0, bindings.Var(span(), scheduler_name))
|
|
194
|
+
else:
|
|
195
|
+
name = "run"
|
|
196
|
+
if until:
|
|
197
|
+
if len(until) > 1:
|
|
198
|
+
msg = "Can only have one until fact with custom scheduler"
|
|
199
|
+
raise ValueError(msg)
|
|
200
|
+
args.append(bindings.Var(span(), ":until"))
|
|
201
|
+
fact_egg = self.fact_to_egg(until[0])
|
|
202
|
+
if isinstance(fact_egg, bindings.Eq):
|
|
203
|
+
msg = "Cannot use equality fact with custom scheduler"
|
|
204
|
+
raise ValueError(msg)
|
|
205
|
+
args.append(fact_egg.expr)
|
|
206
|
+
return [bindings.Call(span(), name, args)]
|
|
207
|
+
case SaturateDecl(inner):
|
|
208
|
+
return [
|
|
209
|
+
bindings.Call(span(), "saturate", self._schedule_with_scheduler_to_egg(inner, bound_schedulers))
|
|
210
|
+
]
|
|
211
|
+
case RepeatDecl(inner, times):
|
|
212
|
+
return [
|
|
213
|
+
bindings.Call(
|
|
214
|
+
span(),
|
|
215
|
+
"repeat",
|
|
216
|
+
[
|
|
217
|
+
bindings.Lit(span(), bindings.Int(times)),
|
|
218
|
+
*self._schedule_with_scheduler_to_egg(inner, bound_schedulers),
|
|
219
|
+
],
|
|
220
|
+
)
|
|
221
|
+
]
|
|
222
|
+
case SequenceDecl(schedules):
|
|
223
|
+
res = []
|
|
224
|
+
for s in schedules:
|
|
225
|
+
res.extend(self._schedule_with_scheduler_to_egg(s, bound_schedulers))
|
|
226
|
+
return res
|
|
104
227
|
case _:
|
|
105
228
|
assert_never(schedule)
|
|
106
229
|
|
egglog/examples/jointree.py
CHANGED
egglog/pretty.py
CHANGED
|
@@ -67,7 +67,9 @@ UNARY_METHODS = {
|
|
|
67
67
|
"__invert__": "~",
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
AllDecls: TypeAlias =
|
|
70
|
+
AllDecls: TypeAlias = (
|
|
71
|
+
RulesetDecl | CombinedRulesetDecl | CommandDecl | ActionDecl | FactDecl | ExprDecl | ScheduleDecl | BackOffDecl
|
|
72
|
+
)
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
def pretty_decl(
|
|
@@ -188,10 +190,12 @@ class TraverseContext:
|
|
|
188
190
|
case _:
|
|
189
191
|
for e in exprs:
|
|
190
192
|
self(e.expr)
|
|
191
|
-
case RunDecl(_, until):
|
|
193
|
+
case RunDecl(_, until, scheduler):
|
|
192
194
|
if until:
|
|
193
195
|
for f in until:
|
|
194
196
|
self(f)
|
|
197
|
+
if scheduler:
|
|
198
|
+
self(scheduler)
|
|
195
199
|
case PartialCallDecl(c):
|
|
196
200
|
self(c)
|
|
197
201
|
case CombinedRulesetDecl(_):
|
|
@@ -201,6 +205,12 @@ class TraverseContext:
|
|
|
201
205
|
case SetCostDecl(_, e, c):
|
|
202
206
|
self(e)
|
|
203
207
|
self(c)
|
|
208
|
+
case BackOffDecl():
|
|
209
|
+
pass
|
|
210
|
+
case LetSchedulerDecl(scheduler, schedule):
|
|
211
|
+
self(scheduler)
|
|
212
|
+
self(schedule)
|
|
213
|
+
|
|
204
214
|
case _:
|
|
205
215
|
assert_never(decl)
|
|
206
216
|
|
|
@@ -238,7 +248,11 @@ class PrettyContext:
|
|
|
238
248
|
# it would take up is > than some constant (~ line length).
|
|
239
249
|
line_diff: int = len(expr) - LINE_DIFFERENCE
|
|
240
250
|
n_parents = self.parents[decl]
|
|
241
|
-
if n_parents > 1 and
|
|
251
|
+
if n_parents > 1 and (
|
|
252
|
+
n_parents * line_diff > MAX_LINE_LENGTH
|
|
253
|
+
# Schedulers with multiple parents need to be the same object, b/c are created with hidden UUIDs
|
|
254
|
+
or tp_name == "scheduler"
|
|
255
|
+
):
|
|
242
256
|
self.names[decl] = expr_name = self._name_expr(tp_name, expr, copy_identifier=False)
|
|
243
257
|
return expr_name
|
|
244
258
|
return expr
|
|
@@ -318,16 +332,27 @@ class PrettyContext:
|
|
|
318
332
|
return f"{self(schedules[0], parens=True)} + {self(schedules[1], parens=True)}", "schedule"
|
|
319
333
|
args = ", ".join(map(self, schedules))
|
|
320
334
|
return f"seq({args})", "schedule"
|
|
321
|
-
case
|
|
335
|
+
case LetSchedulerDecl(scheduler, schedule):
|
|
336
|
+
return f"{self(scheduler, parens=True)}.scope({self(schedule, parens=True)})", "schedule"
|
|
337
|
+
case RunDecl(ruleset_name, until, scheduler):
|
|
322
338
|
ruleset = self.decls._rulesets[ruleset_name]
|
|
323
339
|
ruleset_str = self(ruleset, ruleset_name=ruleset_name)
|
|
324
|
-
if not until:
|
|
340
|
+
if not until and not scheduler:
|
|
325
341
|
return ruleset_str, "schedule"
|
|
326
|
-
|
|
327
|
-
|
|
342
|
+
arg_lst = list(map(self, until or []))
|
|
343
|
+
if scheduler:
|
|
344
|
+
arg_lst.append(f"scheduler={self(scheduler)}")
|
|
345
|
+
return f"run({ruleset_str}, {', '.join(arg_lst)})", "schedule"
|
|
328
346
|
case DefaultRewriteDecl():
|
|
329
347
|
msg = "default rewrites should not be pretty printed"
|
|
330
348
|
raise TypeError(msg)
|
|
349
|
+
case BackOffDecl(_, match_limit, ban_length):
|
|
350
|
+
list_args: list[str] = []
|
|
351
|
+
if match_limit is not None:
|
|
352
|
+
list_args.append(f"match_limit={match_limit}")
|
|
353
|
+
if ban_length is not None:
|
|
354
|
+
list_args.append(f"ban_length={ban_length}")
|
|
355
|
+
return f"back_off({', '.join(list_args)})", "scheduler"
|
|
331
356
|
assert_never(decl)
|
|
332
357
|
|
|
333
358
|
def _call(
|
egglog/runtime.py
CHANGED
|
@@ -635,14 +635,22 @@ for name in TYPE_DEFINED_METHODS:
|
|
|
635
635
|
|
|
636
636
|
|
|
637
637
|
for name, r_method in itertools.product(NUMERIC_BINARY_METHODS, (False, True)):
|
|
638
|
+
method_name = f"__r{name[2:]}" if r_method else name
|
|
638
639
|
|
|
639
|
-
def _numeric_binary_method(
|
|
640
|
+
def _numeric_binary_method(
|
|
641
|
+
self: object, other: object, name: str = name, r_method: bool = r_method, method_name: str = method_name
|
|
642
|
+
) -> object:
|
|
640
643
|
"""
|
|
641
644
|
Implements numeric binary operations.
|
|
642
645
|
|
|
643
646
|
Tries to find the minimum cost conversion of either the LHS or the RHS, by finding all methods with either
|
|
644
647
|
the LHS or the RHS as exactly the right type and then upcasting the other to that type.
|
|
645
648
|
"""
|
|
649
|
+
# First check if we have a preserved method for this:
|
|
650
|
+
if isinstance(self, RuntimeExpr) and (
|
|
651
|
+
(preserved_method := self.__egg_class_decl__.preserved_methods.get(method_name)) is not None
|
|
652
|
+
):
|
|
653
|
+
return preserved_method.__get__(self)(other)
|
|
646
654
|
# 1. switch if reversed method
|
|
647
655
|
if r_method:
|
|
648
656
|
self, other = other, self
|
|
@@ -668,7 +676,6 @@ for name, r_method in itertools.product(NUMERIC_BINARY_METHODS, (False, True)):
|
|
|
668
676
|
fn = RuntimeFunction(Thunk.value(self.__egg_decls__), Thunk.value(method_ref), self)
|
|
669
677
|
return fn(other)
|
|
670
678
|
|
|
671
|
-
method_name = f"__r{name[2:]}" if r_method else name
|
|
672
679
|
setattr(RuntimeExpr, method_name, _numeric_binary_method)
|
|
673
680
|
|
|
674
681
|
|
|
@@ -688,6 +695,8 @@ def resolve_callable(callable: object) -> tuple[CallableRef, Declarations]:
|
|
|
688
695
|
):
|
|
689
696
|
raise NotImplementedError(f"Can only turn constants or classvars into callable refs, not {expr}")
|
|
690
697
|
return expr.callable, decl_thunk()
|
|
698
|
+
case types.MethodWrapperType() if isinstance((slf := callable.__self__), RuntimeClass):
|
|
699
|
+
return MethodRef(slf.__egg_tp__.name, callable.__name__), slf.__egg_decls__
|
|
691
700
|
case _:
|
|
692
701
|
raise NotImplementedError(f"Cannot turn {callable} of type {type(callable)} into a callable ref")
|
|
693
702
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: egglog
|
|
3
|
-
Version: 11.
|
|
3
|
+
Version: 11.3.0
|
|
4
4
|
Classifier: Environment :: MacOS X
|
|
5
5
|
Classifier: Environment :: Win32 (MS Windows)
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -51,6 +51,7 @@ Requires-Dist: egglog[array] ; extra == 'docs'
|
|
|
51
51
|
Requires-Dist: line-profiler ; extra == 'docs'
|
|
52
52
|
Requires-Dist: sphinxcontrib-mermaid ; extra == 'docs'
|
|
53
53
|
Requires-Dist: ablog ; extra == 'docs'
|
|
54
|
+
Requires-Dist: jupytext ; extra == 'docs'
|
|
54
55
|
Provides-Extra: array
|
|
55
56
|
Provides-Extra: dev
|
|
56
57
|
Provides-Extra: test
|
|
@@ -72,3 +73,20 @@ Please see the [documentation](https://egglog-python.readthedocs.io/) for more i
|
|
|
72
73
|
|
|
73
74
|
Come say hello [on the e-graphs Zulip](https://egraphs.zulipchat.com/#narrow/stream/375765-egglog/) or [open an issue](https://github.com/egraphs-good/egglog-python/issues/new/choose)!
|
|
74
75
|
|
|
76
|
+
## How to cite
|
|
77
|
+
|
|
78
|
+
If you use **egglog-python** in academic work, please cite the paper:
|
|
79
|
+
|
|
80
|
+
```bibtex
|
|
81
|
+
@misc{Shanabrook2023EgglogPython,
|
|
82
|
+
title = {Egglog Python: A Pythonic Library for E-graphs},
|
|
83
|
+
author = {Saul Shanabrook},
|
|
84
|
+
year = {2023},
|
|
85
|
+
eprint = {2305.04311},
|
|
86
|
+
archivePrefix = {arXiv},
|
|
87
|
+
primaryClass = {cs.PL},
|
|
88
|
+
doi = {10.48550/arXiv.2305.04311},
|
|
89
|
+
url = {https://arxiv.org/abs/2305.04311},
|
|
90
|
+
note = {Presented at EGRAPHS@PLDI 2023}
|
|
91
|
+
}
|
|
92
|
+
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
egglog-11.
|
|
2
|
-
egglog-11.
|
|
3
|
-
egglog-11.
|
|
1
|
+
egglog-11.3.0.dist-info/METADATA,sha256=_7z3OUAk7vWdO7bwhHHUwhirFqvbHsguTnkGY9qvidQ,4554
|
|
2
|
+
egglog-11.3.0.dist-info/WHEEL,sha256=wvPMIrp4MafmZyLFFKa_Gu58t57iXPruL8GrrDqQ9eI,131
|
|
3
|
+
egglog-11.3.0.dist-info/licenses/LICENSE,sha256=w7VlVv5O_FPZRo8Z-4Zb_q7D5ac3YDs8JUkMZ4Gq9CE,1070
|
|
4
4
|
egglog/__init__.py,sha256=0r3MzQbU-9U0fSCeAoJ3deVhZ77tI-1tf8A_WFOhbJs,344
|
|
5
|
-
egglog/bindings.cpython-313-aarch64-linux-gnu.so,sha256=
|
|
5
|
+
egglog/bindings.cpython-313-aarch64-linux-gnu.so,sha256=_sf35E4AyaWLUm2IOer9wlMx3_ckHY9ZczRGBUK2xik,165849544
|
|
6
6
|
egglog/bindings.pyi,sha256=Y_YpdAKmVHZ0nIHTTPeg0sigBEPiS8z_U-Z161zKSK4,15330
|
|
7
|
-
egglog/builtins.py,sha256=
|
|
7
|
+
egglog/builtins.py,sha256=qXbBOtT1qwgR9uQu9yb1gUp4dm2L6BgvJIWYU4zCzuw,30317
|
|
8
8
|
egglog/config.py,sha256=yM3FIcVCKnhWZmHD0pxkzx7ah7t5PxZx3WUqKtA9tjU,168
|
|
9
9
|
egglog/conversion.py,sha256=DO76lxRbbTqHs6hRo_Lckvtwu0c6LaKoX7k5_B2AfuY,11238
|
|
10
|
-
egglog/declarations.py,sha256=
|
|
10
|
+
egglog/declarations.py,sha256=pc2KEYwyKNQXuKndbBCC6iuVROgHkaSKJJf_s9liZi8,26260
|
|
11
11
|
egglog/deconstruct.py,sha256=CovORrpROMIwOLgERPUw8doqRUDUehj6LJEB5FMbpmI,5635
|
|
12
|
-
egglog/egraph.py,sha256=
|
|
13
|
-
egglog/egraph_state.py,sha256=
|
|
12
|
+
egglog/egraph.py,sha256=zJpAoC6JXXqnRsp24CvQN5M5EZ0PrOj93R9U4w6bqlw,65417
|
|
13
|
+
egglog/egraph_state.py,sha256=3VLwkAsR3oCydHLx_BXmFw4UHXgdZ9jooQdWUcQeUD0,36375
|
|
14
14
|
egglog/examples/README.rst,sha256=ztTvpofR0eotSqGoCy_C1fPLDPCncjvcqDanXtLHNNU,232
|
|
15
15
|
egglog/examples/__init__.py,sha256=wm9evUbMPfbtylXIjbDdRTAVMLH4OjT4Z77PCBFyaPU,31
|
|
16
16
|
egglog/examples/bignum.py,sha256=jfL57XXpQqIqizQQ3sSUCCjTrkdjtB71BmjrQIQorQk,535
|
|
@@ -18,7 +18,7 @@ egglog/examples/bool.py,sha256=e0z2YoYJsLlhpSADZK1yRYHzilyxSZWGiYAaM0DQ_Gw,695
|
|
|
18
18
|
egglog/examples/eqsat_basic.py,sha256=2xtM81gG9Br72mr58N-2BUeksR7C_UXnZJ4MvzSPplc,869
|
|
19
19
|
egglog/examples/fib.py,sha256=BOHxKWA7jGx4FURBmfmuZKfLo6xq9-uXAwAXjYid7LU,492
|
|
20
20
|
egglog/examples/higher_order_functions.py,sha256=DNLIQfPJCX_DOLbHNiaYsfvcFIYCYOsRUqp99r9bpc8,1063
|
|
21
|
-
egglog/examples/jointree.py,sha256=
|
|
21
|
+
egglog/examples/jointree.py,sha256=TLlH7TsQzWfadqDo7qeTprFhLdQmj59AQaGse81RIKk,1714
|
|
22
22
|
egglog/examples/lambda_.py,sha256=iQvwaXVhp2VNOMS7j1WwceZaiq3dqqilwUkMcW5GFBE,8194
|
|
23
23
|
egglog/examples/matrix.py,sha256=7_mPcMcgE-t_GJDyf76-nv3xhPIeN2mvFkc_p_Gnr8g,4961
|
|
24
24
|
egglog/examples/multiset.py,sha256=IBOsB80DkXQ07dYnk1odi27q77LH80Z8zysuLE-Q8F8,1445
|
|
@@ -34,13 +34,13 @@ egglog/exp/array_api_program_gen.py,sha256=qnve8iqklRQVyGChllG8ZAjAffRpezmdxc3Id
|
|
|
34
34
|
egglog/exp/program_gen.py,sha256=CavsD70x0ERS87V4OU9zkgMvLXswGEpb1ZZFK0WyN_g,13033
|
|
35
35
|
egglog/exp/siu_examples.py,sha256=yZ-sgH2Y12iTdwBUumP7D2OtCGL83M6pPW7PMobVFXc,719
|
|
36
36
|
egglog/ipython_magic.py,sha256=2hs3g2cSiyDmbCvE2t1OINmu17Bb8MWV--2DpEWwO7I,1189
|
|
37
|
-
egglog/pretty.py,sha256=
|
|
37
|
+
egglog/pretty.py,sha256=Sv3H9e0CJcZv3-ylijP58ApCQ5w1BOdXl2VDw6Hst4Y,22061
|
|
38
38
|
egglog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
-
egglog/runtime.py,sha256=
|
|
39
|
+
egglog/runtime.py,sha256=NUA0O-_urneP54RqXRcPLQIlFzNwPacPKIMxGpwAkus,29672
|
|
40
40
|
egglog/thunk.py,sha256=MrAlPoGK36VQrUrq8PWSaJFu42sPL0yupwiH18lNips,2271
|
|
41
41
|
egglog/type_constraint_solver.py,sha256=U2GjLgbebTLv5QY8_TU0As5wMKL5_NxkHLen9rpfMwI,4518
|
|
42
42
|
egglog/version_compat.py,sha256=EaKRMIOPcatrx9XjCofxZD6Nr5WOooiWNdoapkKleww,3512
|
|
43
43
|
egglog/visualizer.css,sha256=eL0POoThQRc0P4OYnDT-d808ln9O5Qy6DizH9Z5LgWc,259398
|
|
44
44
|
egglog/visualizer.js,sha256=2qZZ-9W_INJx4gZMYjnVXl27IjT_JNuQyEeI2dbjWoU,3753315
|
|
45
45
|
egglog/visualizer_widget.py,sha256=LtVfzOtv2WeKtNuILQQ_9SOHWvRr8YdBYQDKQSgry_s,1319
|
|
46
|
-
egglog-11.
|
|
46
|
+
egglog-11.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|