egglog 6.1.0__cp310-none-win_amd64.whl → 7.0.0__cp310-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.cp310-win_amd64.pyd +0 -0
- egglog/bindings.pyi +2 -0
- egglog/builtins.py +1 -1
- egglog/conversion.py +172 -0
- egglog/declarations.py +329 -735
- egglog/egraph.py +531 -804
- egglog/egraph_state.py +417 -0
- egglog/exp/array_api.py +92 -80
- egglog/exp/array_api_numba.py +6 -1
- 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.1.0.dist-info → egglog-7.0.0.dist-info}/METADATA +19 -19
- {egglog-6.1.0.dist-info → egglog-7.0.0.dist-info}/RECORD +19 -14
- {egglog-6.1.0.dist-info → egglog-7.0.0.dist-info}/WHEEL +0 -0
- {egglog-6.1.0.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: ...
|
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
|