egglog 6.0.1__cp311-none-win_amd64.whl → 7.0.0__cp311-none-win_amd64.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/__init__.py +1 -1
- egglog/bindings.cp311-win_amd64.pyd +0 -0
- egglog/bindings.pyi +24 -4
- egglog/builtins.py +1 -1
- egglog/conversion.py +172 -0
- egglog/declarations.py +329 -735
- egglog/egraph.py +539 -803
- egglog/egraph_state.py +417 -0
- egglog/exp/array_api.py +96 -84
- egglog/exp/array_api_numba.py +13 -11
- egglog/exp/siu_examples.py +35 -0
- egglog/pretty.py +418 -0
- egglog/runtime.py +196 -430
- egglog/thunk.py +72 -0
- egglog/type_constraint_solver.py +5 -2
- {egglog-6.0.1.dist-info → egglog-7.0.0.dist-info}/METADATA +14 -14
- {egglog-6.0.1.dist-info → egglog-7.0.0.dist-info}/RECORD +19 -14
- {egglog-6.0.1.dist-info → egglog-7.0.0.dist-info}/WHEEL +0 -0
- {egglog-6.0.1.dist-info → egglog-7.0.0.dist-info}/license_files/LICENSE +0 -0
egglog/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ Package for creating e-graphs in Python.
|
|
|
4
4
|
|
|
5
5
|
from . import config, ipython_magic # noqa: F401
|
|
6
6
|
from .builtins import * # noqa: UP029
|
|
7
|
+
from .conversion import convert, converter # noqa: F401
|
|
7
8
|
from .egraph import *
|
|
8
|
-
from .runtime import convert, converter # noqa: F401
|
|
9
9
|
|
|
10
10
|
del ipython_magic
|
|
Binary file
|
egglog/bindings.pyi
CHANGED
|
@@ -29,7 +29,9 @@ class EGraph:
|
|
|
29
29
|
fact_directory: str | Path | None = None,
|
|
30
30
|
seminaive: bool = True,
|
|
31
31
|
terms_encoding: bool = False,
|
|
32
|
+
record: bool = False,
|
|
32
33
|
) -> None: ...
|
|
34
|
+
def commands(self) -> str | None: ...
|
|
33
35
|
def parse_program(self, __input: str, /) -> list[_Command]: ...
|
|
34
36
|
def run_program(self, *commands: _Command) -> list[str]: ...
|
|
35
37
|
def extract_report(self) -> _ExtractReport | None: ...
|
|
@@ -150,6 +152,20 @@ class Fact:
|
|
|
150
152
|
|
|
151
153
|
_Fact: TypeAlias = Fact | Eq
|
|
152
154
|
|
|
155
|
+
##
|
|
156
|
+
# Change
|
|
157
|
+
##
|
|
158
|
+
|
|
159
|
+
@final
|
|
160
|
+
class Delete:
|
|
161
|
+
def __init__(self) -> None: ...
|
|
162
|
+
|
|
163
|
+
@final
|
|
164
|
+
class Subsume:
|
|
165
|
+
def __init__(self) -> None: ...
|
|
166
|
+
|
|
167
|
+
_Change: TypeAlias = Delete | Subsume
|
|
168
|
+
|
|
153
169
|
##
|
|
154
170
|
# Actions
|
|
155
171
|
##
|
|
@@ -168,10 +184,11 @@ class Set:
|
|
|
168
184
|
rhs: _Expr
|
|
169
185
|
|
|
170
186
|
@final
|
|
171
|
-
class
|
|
187
|
+
class Change:
|
|
188
|
+
change: _Change
|
|
172
189
|
sym: str
|
|
173
190
|
args: list[_Expr]
|
|
174
|
-
def __init__(self, sym: str, args: list[_Expr]) -> None: ...
|
|
191
|
+
def __init__(self, change: _Change, sym: str, args: list[_Expr]) -> None: ...
|
|
175
192
|
|
|
176
193
|
@final
|
|
177
194
|
class Union:
|
|
@@ -195,7 +212,7 @@ class Extract:
|
|
|
195
212
|
expr: _Expr
|
|
196
213
|
variants: _Expr
|
|
197
214
|
|
|
198
|
-
_Action: TypeAlias = Let | Set |
|
|
215
|
+
_Action: TypeAlias = Let | Set | Change | Union | Panic | Expr_ | Extract
|
|
199
216
|
|
|
200
217
|
##
|
|
201
218
|
# Other Structs
|
|
@@ -210,6 +227,7 @@ class FunctionDecl:
|
|
|
210
227
|
merge_action: list[_Action]
|
|
211
228
|
cost: int | None
|
|
212
229
|
unextractable: bool
|
|
230
|
+
ignore_viz: bool
|
|
213
231
|
|
|
214
232
|
def __init__(
|
|
215
233
|
self,
|
|
@@ -220,6 +238,7 @@ class FunctionDecl:
|
|
|
220
238
|
merge_action: list[_Action] = [], # noqa: B006
|
|
221
239
|
cost: int | None = None,
|
|
222
240
|
unextractable: bool = False,
|
|
241
|
+
ignore_viz: bool = False,
|
|
223
242
|
) -> None: ...
|
|
224
243
|
|
|
225
244
|
@final
|
|
@@ -374,7 +393,8 @@ class RewriteCommand:
|
|
|
374
393
|
# TODO: Rename to ruleset
|
|
375
394
|
name: str
|
|
376
395
|
rewrite: Rewrite
|
|
377
|
-
|
|
396
|
+
subsume: bool
|
|
397
|
+
def __init__(self, name: str, rewrite: Rewrite, subsume: bool) -> None: ...
|
|
378
398
|
|
|
379
399
|
@final
|
|
380
400
|
class BiRewriteCommand:
|
egglog/builtins.py
CHANGED
|
@@ -7,8 +7,8 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
from typing import TYPE_CHECKING, Generic, Protocol, TypeAlias, TypeVar, Union
|
|
9
9
|
|
|
10
|
+
from .conversion import converter
|
|
10
11
|
from .egraph import Expr, Unit, function, method
|
|
11
|
-
from .runtime import converter
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from collections.abc import Callable
|
egglog/conversion.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, TypeVar, cast
|
|
5
|
+
|
|
6
|
+
from .declarations import *
|
|
7
|
+
from .pretty import *
|
|
8
|
+
from .runtime import *
|
|
9
|
+
from .thunk import *
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
|
|
14
|
+
from .declarations import HasDeclerations
|
|
15
|
+
from .egraph import Expr
|
|
16
|
+
|
|
17
|
+
__all__ = ["convert", "converter", "resolve_literal", "convert_to_same_type"]
|
|
18
|
+
# Mapping from (source type, target type) to and function which takes in the runtimes values of the source and return the target
|
|
19
|
+
CONVERSIONS: dict[tuple[type | JustTypeRef, JustTypeRef], tuple[int, Callable]] = {}
|
|
20
|
+
# Global declerations to store all convertable types so we can query if they have certain methods or not
|
|
21
|
+
# Defer it as a thunk so we can register conversions without triggering type signature loading
|
|
22
|
+
CONVERSIONS_DECLS: Callable[[], Declarations] = Thunk.value(Declarations())
|
|
23
|
+
|
|
24
|
+
T = TypeVar("T")
|
|
25
|
+
V = TypeVar("V", bound="Expr")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ConvertError(Exception):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def converter(from_type: type[T], to_type: type[V], fn: Callable[[T], V], cost: int = 1) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Register a converter from some type to an egglog type.
|
|
35
|
+
"""
|
|
36
|
+
to_type_name = process_tp(to_type)
|
|
37
|
+
if not isinstance(to_type_name, JustTypeRef):
|
|
38
|
+
raise TypeError(f"Expected return type to be a egglog type, got {to_type_name}")
|
|
39
|
+
_register_converter(process_tp(from_type), to_type_name, fn, cost)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _register_converter(a: type | JustTypeRef, b: JustTypeRef, a_b: Callable, cost: int) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Registers a converter from some type to an egglog type, if not already registered.
|
|
45
|
+
|
|
46
|
+
Also adds transitive converters, i.e. if registering A->B and there is already B->C, then A->C will be registered.
|
|
47
|
+
Also, if registering A->B and there is already D->A, then D->B will be registered.
|
|
48
|
+
"""
|
|
49
|
+
if a == b:
|
|
50
|
+
return
|
|
51
|
+
if (a, b) in CONVERSIONS and CONVERSIONS[(a, b)][0] <= cost:
|
|
52
|
+
return
|
|
53
|
+
CONVERSIONS[(a, b)] = (cost, a_b)
|
|
54
|
+
for (c, d), (other_cost, c_d) in list(CONVERSIONS.items()):
|
|
55
|
+
if b == c:
|
|
56
|
+
_register_converter(a, d, _ComposedConverter(a_b, c_d), cost + other_cost)
|
|
57
|
+
if a == d:
|
|
58
|
+
_register_converter(c, b, _ComposedConverter(c_d, a_b), cost + other_cost)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class _ComposedConverter:
|
|
63
|
+
"""
|
|
64
|
+
A converter which is composed of multiple converters.
|
|
65
|
+
|
|
66
|
+
_ComposeConverter(a_b, b_c) is equivalent to lambda x: b_c(a_b(x))
|
|
67
|
+
|
|
68
|
+
We use the dataclass instead of the lambda to make it easier to debug.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
a_b: Callable
|
|
72
|
+
b_c: Callable
|
|
73
|
+
|
|
74
|
+
def __call__(self, x: object) -> object:
|
|
75
|
+
return self.b_c(self.a_b(x))
|
|
76
|
+
|
|
77
|
+
def __str__(self) -> str:
|
|
78
|
+
return f"{self.b_c} ∘ {self.a_b}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def convert(source: object, target: type[V]) -> V:
|
|
82
|
+
"""
|
|
83
|
+
Convert a source object to a target type.
|
|
84
|
+
"""
|
|
85
|
+
assert isinstance(target, RuntimeClass)
|
|
86
|
+
return cast(V, resolve_literal(target.__egg_tp__, source))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def convert_to_same_type(source: object, target: RuntimeExpr) -> RuntimeExpr:
|
|
90
|
+
"""
|
|
91
|
+
Convert a source object to the same type as the target.
|
|
92
|
+
"""
|
|
93
|
+
tp = target.__egg_typed_expr__.tp
|
|
94
|
+
return resolve_literal(tp.to_var(), source)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def process_tp(tp: type | RuntimeClass) -> JustTypeRef | type:
|
|
98
|
+
"""
|
|
99
|
+
Process a type before converting it, to add it to the global declerations and resolve to a ref.
|
|
100
|
+
"""
|
|
101
|
+
global CONVERSIONS_DECLS
|
|
102
|
+
if isinstance(tp, RuntimeClass):
|
|
103
|
+
CONVERSIONS_DECLS = Thunk.fn(_combine_decls, CONVERSIONS_DECLS, tp)
|
|
104
|
+
return tp.__egg_tp__.to_just()
|
|
105
|
+
return tp
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _combine_decls(d: Callable[[], Declarations], x: HasDeclerations) -> Declarations:
|
|
109
|
+
return Declarations.create(d(), x)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def min_convertable_tp(a: object, b: object, name: str) -> JustTypeRef:
|
|
113
|
+
"""
|
|
114
|
+
Returns the minimum convertable type between a and b, that has a method `name`, raising a ConvertError if no such type exists.
|
|
115
|
+
"""
|
|
116
|
+
decls = CONVERSIONS_DECLS()
|
|
117
|
+
a_tp = _get_tp(a)
|
|
118
|
+
b_tp = _get_tp(b)
|
|
119
|
+
a_converts_to = {
|
|
120
|
+
to: c for ((from_, to), (c, _)) in CONVERSIONS.items() if from_ == a_tp and decls.has_method(to.name, name)
|
|
121
|
+
}
|
|
122
|
+
b_converts_to = {
|
|
123
|
+
to: c for ((from_, to), (c, _)) in CONVERSIONS.items() if from_ == b_tp and decls.has_method(to.name, name)
|
|
124
|
+
}
|
|
125
|
+
if isinstance(a_tp, JustTypeRef):
|
|
126
|
+
a_converts_to[a_tp] = 0
|
|
127
|
+
if isinstance(b_tp, JustTypeRef):
|
|
128
|
+
b_converts_to[b_tp] = 0
|
|
129
|
+
common = set(a_converts_to) & set(b_converts_to)
|
|
130
|
+
if not common:
|
|
131
|
+
raise ConvertError(f"Cannot convert {a_tp} and {b_tp} to a common type")
|
|
132
|
+
return min(common, key=lambda tp: a_converts_to[tp] + b_converts_to[tp])
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def identity(x: object) -> object:
|
|
136
|
+
return x
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def resolve_literal(tp: TypeOrVarRef, arg: object) -> RuntimeExpr:
|
|
140
|
+
arg_type = _get_tp(arg)
|
|
141
|
+
|
|
142
|
+
# If we have any type variables, dont bother trying to resolve the literal, just return the arg
|
|
143
|
+
try:
|
|
144
|
+
tp_just = tp.to_just()
|
|
145
|
+
except NotImplementedError:
|
|
146
|
+
# If this is a var, it has to be a runtime exprssions
|
|
147
|
+
assert isinstance(arg, RuntimeExpr)
|
|
148
|
+
return arg
|
|
149
|
+
if arg_type == tp_just:
|
|
150
|
+
# If the type is an egg type, it has to be a runtime expr
|
|
151
|
+
assert isinstance(arg, RuntimeExpr)
|
|
152
|
+
return arg
|
|
153
|
+
# Try all parent types as well, if we are converting from a Python type
|
|
154
|
+
for arg_type_instance in arg_type.__mro__ if isinstance(arg_type, type) else [arg_type]:
|
|
155
|
+
try:
|
|
156
|
+
fn = CONVERSIONS[(cast(JustTypeRef | type, arg_type_instance), tp_just)][1]
|
|
157
|
+
except KeyError:
|
|
158
|
+
continue
|
|
159
|
+
break
|
|
160
|
+
else:
|
|
161
|
+
raise ConvertError(f"Cannot convert {arg_type} to {tp_just}")
|
|
162
|
+
return fn(arg)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _get_tp(x: object) -> JustTypeRef | type:
|
|
166
|
+
if isinstance(x, RuntimeExpr):
|
|
167
|
+
return x.__egg_typed_expr__.tp
|
|
168
|
+
tp = type(x)
|
|
169
|
+
# If this value has a custom metaclass, let's use that as our index instead of the type
|
|
170
|
+
if type(tp) != type:
|
|
171
|
+
return type(tp)
|
|
172
|
+
return tp
|