egglog 6.1.0__cp310-none-win_amd64.whl → 7.1.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 +9 -0
- egglog/builtins.py +42 -2
- egglog/conversion.py +177 -0
- egglog/declarations.py +354 -734
- egglog/egraph.py +602 -800
- egglog/egraph_state.py +456 -0
- egglog/exp/array_api.py +100 -88
- egglog/exp/array_api_numba.py +6 -1
- egglog/exp/siu_examples.py +35 -0
- egglog/pretty.py +464 -0
- egglog/runtime.py +279 -431
- egglog/thunk.py +71 -0
- egglog/type_constraint_solver.py +5 -2
- {egglog-6.1.0.dist-info → egglog-7.1.0.dist-info}/METADATA +7 -7
- {egglog-6.1.0.dist-info → egglog-7.1.0.dist-info}/RECORD +19 -14
- {egglog-6.1.0.dist-info → egglog-7.1.0.dist-info}/WHEEL +0 -0
- {egglog-6.1.0.dist-info → egglog-7.1.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: ...
|
|
@@ -492,6 +494,12 @@ class Relation:
|
|
|
492
494
|
class PrintOverallStatistics:
|
|
493
495
|
def __init__(self) -> None: ...
|
|
494
496
|
|
|
497
|
+
@final
|
|
498
|
+
class UnstableCombinedRuleset:
|
|
499
|
+
name: str
|
|
500
|
+
rulesets: list[str]
|
|
501
|
+
def __init__(self, name: str, rulesets: list[str]) -> None: ...
|
|
502
|
+
|
|
495
503
|
_Command: TypeAlias = (
|
|
496
504
|
SetOption
|
|
497
505
|
| Datatype
|
|
@@ -519,6 +527,7 @@ _Command: TypeAlias = (
|
|
|
519
527
|
| CheckProof
|
|
520
528
|
| Relation
|
|
521
529
|
| PrintOverallStatistics
|
|
530
|
+
| UnstableCombinedRuleset
|
|
522
531
|
)
|
|
523
532
|
|
|
524
533
|
def termdag_term_to_expr(termdag: TermDag, term: _Term) -> _Expr: ...
|
egglog/builtins.py
CHANGED
|
@@ -5,10 +5,14 @@ Builtin sorts and function to egg.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from
|
|
8
|
+
from functools import partial
|
|
9
|
+
from typing import TYPE_CHECKING, Generic, Protocol, TypeAlias, TypeVar, Union, overload
|
|
9
10
|
|
|
11
|
+
from typing_extensions import TypeVarTuple, Unpack
|
|
12
|
+
|
|
13
|
+
from .conversion import converter
|
|
10
14
|
from .egraph import Expr, Unit, function, method
|
|
11
|
-
from .runtime import
|
|
15
|
+
from .runtime import RuntimeFunction
|
|
12
16
|
|
|
13
17
|
if TYPE_CHECKING:
|
|
14
18
|
from collections.abc import Callable
|
|
@@ -31,6 +35,7 @@ __all__ = [
|
|
|
31
35
|
"py_eval",
|
|
32
36
|
"py_exec",
|
|
33
37
|
"py_eval_fn",
|
|
38
|
+
"UnstableFn",
|
|
34
39
|
]
|
|
35
40
|
|
|
36
41
|
|
|
@@ -461,3 +466,38 @@ def py_exec(code: StringLike, globals: object = PyObject.dict(), locals: object
|
|
|
461
466
|
"""
|
|
462
467
|
Copies the locals, execs the Python code, and returns the locals with any updates.
|
|
463
468
|
"""
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
TS = TypeVarTuple("TS")
|
|
472
|
+
|
|
473
|
+
T1 = TypeVar("T1")
|
|
474
|
+
T2 = TypeVar("T2")
|
|
475
|
+
T3 = TypeVar("T3")
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class UnstableFn(Expr, Generic[T, Unpack[TS]], builtin=True):
|
|
479
|
+
@overload
|
|
480
|
+
def __init__(self, f: Callable[[Unpack[TS]], T]) -> None: ...
|
|
481
|
+
|
|
482
|
+
@overload
|
|
483
|
+
def __init__(self, f: Callable[[T1, Unpack[TS]], T], _a: T1, /) -> None: ...
|
|
484
|
+
|
|
485
|
+
@overload
|
|
486
|
+
def __init__(self, f: Callable[[T1, T2, Unpack[TS]], T], _a: T1, _b: T2, /) -> None: ...
|
|
487
|
+
|
|
488
|
+
# Removing due to bug in MyPy
|
|
489
|
+
# https://github.com/python/mypy/issues/17212
|
|
490
|
+
# @overload
|
|
491
|
+
# def __init__(self, f: Callable[[T1, T2, T3, Unpack[TS]], T], _a: T1, _b: T2, _c: T3, /) -> None: ...
|
|
492
|
+
|
|
493
|
+
# etc, for partial application
|
|
494
|
+
|
|
495
|
+
@method(egg_fn="unstable-fn")
|
|
496
|
+
def __init__(self, f, *partial) -> None: ...
|
|
497
|
+
|
|
498
|
+
@method(egg_fn="unstable-app")
|
|
499
|
+
def __call__(self, *args: Unpack[TS]) -> T: ...
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
converter(RuntimeFunction, UnstableFn, UnstableFn)
|
|
503
|
+
converter(partial, UnstableFn, lambda p: UnstableFn(p.func, *p.args))
|
egglog/conversion.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, NewType, 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
|
+
TypeName = NewType("TypeName", str)
|
|
20
|
+
CONVERSIONS: dict[tuple[type | TypeName, TypeName], tuple[int, Callable]] = {}
|
|
21
|
+
# Global declerations to store all convertable types so we can query if they have certain methods or not
|
|
22
|
+
# Defer it as a thunk so we can register conversions without triggering type signature loading
|
|
23
|
+
CONVERSIONS_DECLS: Callable[[], Declarations] = Thunk.value(Declarations())
|
|
24
|
+
|
|
25
|
+
T = TypeVar("T")
|
|
26
|
+
V = TypeVar("V", bound="Expr")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ConvertError(Exception):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def converter(from_type: type[T], to_type: type[V], fn: Callable[[T], V], cost: int = 1) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Register a converter from some type to an egglog type.
|
|
36
|
+
"""
|
|
37
|
+
to_type_name = process_tp(to_type)
|
|
38
|
+
if not isinstance(to_type_name, str):
|
|
39
|
+
raise TypeError(f"Expected return type to be a egglog type, got {to_type_name}")
|
|
40
|
+
_register_converter(process_tp(from_type), to_type_name, fn, cost)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _register_converter(a: type | TypeName, b: TypeName, a_b: Callable, cost: int) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Registers a converter from some type to an egglog type, if not already registered.
|
|
46
|
+
|
|
47
|
+
Also adds transitive converters, i.e. if registering A->B and there is already B->C, then A->C will be registered.
|
|
48
|
+
Also, if registering A->B and there is already D->A, then D->B will be registered.
|
|
49
|
+
"""
|
|
50
|
+
if a == b:
|
|
51
|
+
return
|
|
52
|
+
if (a, b) in CONVERSIONS and CONVERSIONS[(a, b)][0] <= cost:
|
|
53
|
+
return
|
|
54
|
+
CONVERSIONS[(a, b)] = (cost, a_b)
|
|
55
|
+
for (c, d), (other_cost, c_d) in list(CONVERSIONS.items()):
|
|
56
|
+
if b == c:
|
|
57
|
+
_register_converter(a, d, _ComposedConverter(a_b, c_d), cost + other_cost)
|
|
58
|
+
if a == d:
|
|
59
|
+
_register_converter(c, b, _ComposedConverter(c_d, a_b), cost + other_cost)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class _ComposedConverter:
|
|
64
|
+
"""
|
|
65
|
+
A converter which is composed of multiple converters.
|
|
66
|
+
|
|
67
|
+
_ComposeConverter(a_b, b_c) is equivalent to lambda x: b_c(a_b(x))
|
|
68
|
+
|
|
69
|
+
We use the dataclass instead of the lambda to make it easier to debug.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
a_b: Callable
|
|
73
|
+
b_c: Callable
|
|
74
|
+
|
|
75
|
+
def __call__(self, x: object) -> object:
|
|
76
|
+
return self.b_c(self.a_b(x))
|
|
77
|
+
|
|
78
|
+
def __str__(self) -> str:
|
|
79
|
+
return f"{self.b_c} ∘ {self.a_b}"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def convert(source: object, target: type[V]) -> V:
|
|
83
|
+
"""
|
|
84
|
+
Convert a source object to a target type.
|
|
85
|
+
"""
|
|
86
|
+
assert isinstance(target, RuntimeClass)
|
|
87
|
+
return cast(V, resolve_literal(target.__egg_tp__, source))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def convert_to_same_type(source: object, target: RuntimeExpr) -> RuntimeExpr:
|
|
91
|
+
"""
|
|
92
|
+
Convert a source object to the same type as the target.
|
|
93
|
+
"""
|
|
94
|
+
tp = target.__egg_typed_expr__.tp
|
|
95
|
+
return resolve_literal(tp.to_var(), source)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def process_tp(tp: type | RuntimeClass) -> TypeName | type:
|
|
99
|
+
"""
|
|
100
|
+
Process a type before converting it, to add it to the global declerations and resolve to a ref.
|
|
101
|
+
"""
|
|
102
|
+
global CONVERSIONS_DECLS
|
|
103
|
+
if isinstance(tp, RuntimeClass):
|
|
104
|
+
CONVERSIONS_DECLS = Thunk.fn(_combine_decls, CONVERSIONS_DECLS, tp)
|
|
105
|
+
egg_tp = tp.__egg_tp__
|
|
106
|
+
if egg_tp.args:
|
|
107
|
+
raise TypeError(f"Cannot register a converter for a generic type, got {tp}")
|
|
108
|
+
return TypeName(egg_tp.name)
|
|
109
|
+
return tp
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _combine_decls(d: Callable[[], Declarations], x: HasDeclerations) -> Declarations:
|
|
113
|
+
return Declarations.create(d(), x)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def min_convertable_tp(a: object, b: object, name: str) -> TypeName:
|
|
117
|
+
"""
|
|
118
|
+
Returns the minimum convertable type between a and b, that has a method `name`, raising a ConvertError if no such type exists.
|
|
119
|
+
"""
|
|
120
|
+
decls = CONVERSIONS_DECLS()
|
|
121
|
+
a_tp = _get_tp(a)
|
|
122
|
+
b_tp = _get_tp(b)
|
|
123
|
+
a_converts_to = {
|
|
124
|
+
to: c for ((from_, to), (c, _)) in CONVERSIONS.items() if from_ == a_tp and decls.has_method(to, name)
|
|
125
|
+
}
|
|
126
|
+
b_converts_to = {
|
|
127
|
+
to: c for ((from_, to), (c, _)) in CONVERSIONS.items() if from_ == b_tp and decls.has_method(to, name)
|
|
128
|
+
}
|
|
129
|
+
if isinstance(a_tp, str):
|
|
130
|
+
a_converts_to[a_tp] = 0
|
|
131
|
+
if isinstance(b_tp, str):
|
|
132
|
+
b_converts_to[b_tp] = 0
|
|
133
|
+
common = set(a_converts_to) & set(b_converts_to)
|
|
134
|
+
if not common:
|
|
135
|
+
raise ConvertError(f"Cannot convert {a_tp} and {b_tp} to a common type")
|
|
136
|
+
return min(common, key=lambda tp: a_converts_to[tp] + b_converts_to[tp])
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def identity(x: object) -> object:
|
|
140
|
+
return x
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def resolve_literal(tp: TypeOrVarRef, arg: object) -> RuntimeExpr:
|
|
144
|
+
arg_type = _get_tp(arg)
|
|
145
|
+
|
|
146
|
+
# If we have any type variables, dont bother trying to resolve the literal, just return the arg
|
|
147
|
+
try:
|
|
148
|
+
tp_just = tp.to_just()
|
|
149
|
+
except NotImplementedError:
|
|
150
|
+
# If this is a var, it has to be a runtime expession
|
|
151
|
+
assert isinstance(arg, RuntimeExpr)
|
|
152
|
+
return arg
|
|
153
|
+
tp_name = TypeName(tp_just.name)
|
|
154
|
+
if arg_type == tp_name:
|
|
155
|
+
# If the type is an egg type, it has to be a runtime expr
|
|
156
|
+
assert isinstance(arg, RuntimeExpr)
|
|
157
|
+
return arg
|
|
158
|
+
# Try all parent types as well, if we are converting from a Python type
|
|
159
|
+
for arg_type_instance in arg_type.__mro__ if isinstance(arg_type, type) else [arg_type]:
|
|
160
|
+
try:
|
|
161
|
+
fn = CONVERSIONS[(cast(TypeName | type, arg_type_instance), tp_name)][1]
|
|
162
|
+
except KeyError:
|
|
163
|
+
continue
|
|
164
|
+
break
|
|
165
|
+
else:
|
|
166
|
+
raise ConvertError(f"Cannot convert {arg_type} to {tp_name}")
|
|
167
|
+
return fn(arg)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _get_tp(x: object) -> TypeName | type:
|
|
171
|
+
if isinstance(x, RuntimeExpr):
|
|
172
|
+
return TypeName(x.__egg_typed_expr__.tp.name)
|
|
173
|
+
tp = type(x)
|
|
174
|
+
# If this value has a custom metaclass, let's use that as our index instead of the type
|
|
175
|
+
if type(tp) != type:
|
|
176
|
+
return type(tp)
|
|
177
|
+
return tp
|