egglog 12.0.0__cp313-cp313t-manylinux_2_17_ppc64.manylinux2014_ppc64.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.
- egglog/__init__.py +13 -0
- egglog/bindings.cpython-313t-powerpc64-linux-gnu.so +0 -0
- egglog/bindings.pyi +887 -0
- egglog/builtins.py +1144 -0
- egglog/config.py +8 -0
- egglog/conversion.py +290 -0
- egglog/declarations.py +964 -0
- egglog/deconstruct.py +176 -0
- egglog/egraph.py +2247 -0
- egglog/egraph_state.py +978 -0
- egglog/examples/README.rst +5 -0
- egglog/examples/__init__.py +3 -0
- egglog/examples/bignum.py +32 -0
- egglog/examples/bool.py +38 -0
- egglog/examples/eqsat_basic.py +44 -0
- egglog/examples/fib.py +28 -0
- egglog/examples/higher_order_functions.py +42 -0
- egglog/examples/jointree.py +64 -0
- egglog/examples/lambda_.py +287 -0
- egglog/examples/matrix.py +175 -0
- egglog/examples/multiset.py +60 -0
- egglog/examples/ndarrays.py +144 -0
- egglog/examples/resolution.py +84 -0
- egglog/examples/schedule_demo.py +34 -0
- egglog/exp/MoA.ipynb +617 -0
- egglog/exp/__init__.py +3 -0
- egglog/exp/any_expr.py +947 -0
- egglog/exp/any_expr_example.ipynb +408 -0
- egglog/exp/array_api.py +2019 -0
- egglog/exp/array_api_jit.py +51 -0
- egglog/exp/array_api_loopnest.py +74 -0
- egglog/exp/array_api_numba.py +69 -0
- egglog/exp/array_api_program_gen.py +510 -0
- egglog/exp/program_gen.py +427 -0
- egglog/exp/siu_examples.py +32 -0
- egglog/ipython_magic.py +41 -0
- egglog/pretty.py +566 -0
- egglog/py.typed +0 -0
- egglog/runtime.py +888 -0
- egglog/thunk.py +97 -0
- egglog/type_constraint_solver.py +111 -0
- egglog/visualizer.css +1 -0
- egglog/visualizer.js +35798 -0
- egglog/visualizer_widget.py +39 -0
- egglog-12.0.0.dist-info/METADATA +93 -0
- egglog-12.0.0.dist-info/RECORD +48 -0
- egglog-12.0.0.dist-info/WHEEL +5 -0
- egglog-12.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# mypy: disable-error-code="empty-body"
|
|
2
|
+
"""
|
|
3
|
+
Builds up imperative string expressions from a functional expression.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import TypeAlias
|
|
9
|
+
|
|
10
|
+
from egglog import *
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Program(Expr):
|
|
14
|
+
"""
|
|
15
|
+
Semanticallly represents an expression with a number of ordered statements that it depends on to run.
|
|
16
|
+
|
|
17
|
+
The expression and statements are all represented as strings.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, expr: StringLike, is_identifier: BoolLike = Bool(False)) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Create a program based on a string expression.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __add__(self, other: ProgramLike) -> Program:
|
|
26
|
+
"""
|
|
27
|
+
Concats the strings of the two expressions and also the statements.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __radd__(self, other: ProgramLike) -> Program: ...
|
|
31
|
+
|
|
32
|
+
@method(unextractable=True)
|
|
33
|
+
def statement(self, statement: ProgramLike) -> Program:
|
|
34
|
+
"""
|
|
35
|
+
Uses the expression of the statement and adds that as a statement to the program.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def assign(self) -> Program:
|
|
39
|
+
"""
|
|
40
|
+
Returns a new program with the expression assigned to a gensym.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def function_two(self, arg1: ProgramLike, arg2: ProgramLike, name: StringLike = String("__fn")) -> Program:
|
|
44
|
+
"""
|
|
45
|
+
Returns a new program defining a function with two arguments.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def function_three(
|
|
49
|
+
self, arg1: ProgramLike, arg2: ProgramLike, arg3: ProgramLike, name: StringLike = String("__fn")
|
|
50
|
+
) -> Program:
|
|
51
|
+
"""
|
|
52
|
+
Returns a new program defining a function with three arguments.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def expr_to_statement(self) -> Program:
|
|
56
|
+
"""
|
|
57
|
+
Returns a new program with the expression as a statement and the new expression empty.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def expr(self) -> String:
|
|
62
|
+
"""
|
|
63
|
+
Returns the expression of the program, if it's been compiled
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def statements(self) -> String:
|
|
68
|
+
"""
|
|
69
|
+
Returns the statements of the program, if it's been compiled
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def next_sym(self) -> i64:
|
|
74
|
+
"""
|
|
75
|
+
Returns the next gensym to use. This is set after calling `compile(i)` on a program.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# TODO: Replace w/ def next_sym(self) -> i64: ... ?
|
|
79
|
+
def compile(self, next_sym: i64 = i64(0)) -> Unit:
|
|
80
|
+
"""
|
|
81
|
+
Triggers compilation of the program.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
@method(merge=lambda old, _new: old) # type: ignore[prop-decorator]
|
|
85
|
+
@property
|
|
86
|
+
def parent(self) -> Program:
|
|
87
|
+
"""
|
|
88
|
+
Returns the parent of the program, if it's been compiled into the parent.
|
|
89
|
+
|
|
90
|
+
Only keeps the original parent, not any additional ones, so that each set of statements is only added once.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def is_identifer(self) -> Bool:
|
|
95
|
+
"""
|
|
96
|
+
Returns whether the expression is an identifier. Used so that we don't re-assign any identifiers.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
ProgramLike: TypeAlias = Program | StringLike
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
converter(String, Program, Program)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class EvalProgram(Expr):
|
|
107
|
+
def __init__(self, program: Program, globals: object) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Evaluates the program and saves as the py_object
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
# Only allow it to be set once, b/c hash of functions not stable
|
|
113
|
+
@method(merge=lambda old, _new: old) # type: ignore[prop-decorator]
|
|
114
|
+
@property
|
|
115
|
+
def as_py_object(self) -> PyObject:
|
|
116
|
+
"""
|
|
117
|
+
Returns the python object of the program, if it's been evaluated.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@ruleset
|
|
122
|
+
def eval_program_rulseset(ep: EvalProgram, p: Program, expr: String, statements: String, g: PyObject):
|
|
123
|
+
# When we evaluate a program, we first want to compile to a string
|
|
124
|
+
yield rule(EvalProgram(p, g)).then(p.compile())
|
|
125
|
+
# Then we want to evaluate the statements/expr
|
|
126
|
+
yield rule(
|
|
127
|
+
eq(ep).to(EvalProgram(p, g)),
|
|
128
|
+
eq(p.statements).to(statements),
|
|
129
|
+
eq(p.expr).to(expr),
|
|
130
|
+
).then(
|
|
131
|
+
set_(ep.as_py_object).to(
|
|
132
|
+
py_eval(
|
|
133
|
+
"l['___res']",
|
|
134
|
+
PyObject.dict(PyObject.from_string("l"), py_exec(join(statements, "\n", "___res = ", expr), g)),
|
|
135
|
+
)
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@ruleset
|
|
141
|
+
def program_gen_ruleset(
|
|
142
|
+
s: String,
|
|
143
|
+
s1: String,
|
|
144
|
+
s2: String,
|
|
145
|
+
s3: String,
|
|
146
|
+
s4: String,
|
|
147
|
+
s5: String,
|
|
148
|
+
s6: String,
|
|
149
|
+
p: Program,
|
|
150
|
+
p1: Program,
|
|
151
|
+
p2: Program,
|
|
152
|
+
p3: Program,
|
|
153
|
+
p4: Program,
|
|
154
|
+
i: i64,
|
|
155
|
+
i2: i64,
|
|
156
|
+
b: Bool,
|
|
157
|
+
):
|
|
158
|
+
##
|
|
159
|
+
# Expression
|
|
160
|
+
##
|
|
161
|
+
|
|
162
|
+
# Compiling a string just gives that string
|
|
163
|
+
yield rule(eq(p).to(Program(s, b)), p.compile(i)).then(
|
|
164
|
+
set_(p.expr).to(s),
|
|
165
|
+
set_(p.statements).to(String("")),
|
|
166
|
+
set_(p.next_sym).to(i),
|
|
167
|
+
set_(p.is_identifer).to(b),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
##
|
|
171
|
+
# Statement
|
|
172
|
+
##
|
|
173
|
+
# Compiling a statement means that we should use the expression of the statement as a statement and use the expression of the first
|
|
174
|
+
yield rewrite(p1.statement(p2)).to(p1 + p2.expr_to_statement())
|
|
175
|
+
|
|
176
|
+
##
|
|
177
|
+
# Expr to statement
|
|
178
|
+
##
|
|
179
|
+
stmt = eq(p).to(p1.expr_to_statement())
|
|
180
|
+
# 1. Set parent and is_identifier to false, since its empty
|
|
181
|
+
yield rule(stmt, p.compile(i)).then(set_(p1.parent).to(p), set_(p.is_identifer).to(Bool(False)))
|
|
182
|
+
# 2. Compile p1 if parent set
|
|
183
|
+
yield rule(stmt, p.compile(i), eq(p1.parent).to(p)).then(p1.compile(i))
|
|
184
|
+
# 3.a. If parent not set, set statements to expr
|
|
185
|
+
yield rule(
|
|
186
|
+
stmt,
|
|
187
|
+
p.compile(i),
|
|
188
|
+
ne(p1.parent).to(p),
|
|
189
|
+
eq(s1).to(p1.expr),
|
|
190
|
+
).then(
|
|
191
|
+
set_(p.statements).to(join(s1, "\n")),
|
|
192
|
+
set_(p.next_sym).to(i),
|
|
193
|
+
set_(p.expr).to(String("")),
|
|
194
|
+
)
|
|
195
|
+
# 3.b. If parent set, set statements to expr + statements
|
|
196
|
+
yield rule(
|
|
197
|
+
stmt,
|
|
198
|
+
eq(p1.parent).to(p),
|
|
199
|
+
eq(s1).to(p1.expr),
|
|
200
|
+
eq(s2).to(p1.statements),
|
|
201
|
+
eq(i).to(p1.next_sym),
|
|
202
|
+
).then(
|
|
203
|
+
set_(p.statements).to(join(s2, s1, "\n")),
|
|
204
|
+
set_(p.next_sym).to(i),
|
|
205
|
+
set_(p.expr).to(String("")),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# Addition
|
|
210
|
+
##
|
|
211
|
+
|
|
212
|
+
# Compiling an addition is the same as compiling one, then the other, then setting the expression as the addition
|
|
213
|
+
# of the two
|
|
214
|
+
program_add = eq(p).to(p1 + p2)
|
|
215
|
+
|
|
216
|
+
# If the resulting expression is either of the inputs, then its an identifer if those are
|
|
217
|
+
# Otherwise, if its not equal to either input, its not an identifier
|
|
218
|
+
yield rule(program_add, eq(p.expr).to(p1.expr), eq(b).to(p1.is_identifer)).then(set_(p.is_identifer).to(b))
|
|
219
|
+
yield rule(program_add, eq(p.expr).to(p2.expr), eq(b).to(p2.is_identifer)).then(set_(p.is_identifer).to(b))
|
|
220
|
+
yield rule(program_add, ne(p.expr).to(p1.expr), ne(p.expr).to(p2.expr)).then(set_(p.is_identifer).to(Bool(False)))
|
|
221
|
+
|
|
222
|
+
# Set parent of p1
|
|
223
|
+
yield rule(program_add, p.compile(i)).then(
|
|
224
|
+
set_(p1.parent).to(p),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Compile p1, if p1 parent equal
|
|
228
|
+
yield rule(program_add, p.compile(i), eq(p1.parent).to(p)).then(p1.compile(i))
|
|
229
|
+
|
|
230
|
+
# Set parent of p2, once p1 compiled
|
|
231
|
+
yield rule(program_add, p.compile(i), p1.next_sym).then(set_(p2.parent).to(p))
|
|
232
|
+
|
|
233
|
+
# Compile p2, if p1 parent not equal, but p2 parent equal
|
|
234
|
+
yield rule(program_add, p.compile(i), ne(p1.parent).to(p), eq(p2.parent).to(p)).then(p2.compile(i))
|
|
235
|
+
|
|
236
|
+
# Compile p2, if p1 parent eqal
|
|
237
|
+
yield rule(program_add, p.compile(i2), eq(p1.parent).to(p), eq(i).to(p1.next_sym), eq(p2.parent).to(p)).then(
|
|
238
|
+
p2.compile(i)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Set p expr to join of p1 and p2
|
|
242
|
+
yield rule(
|
|
243
|
+
program_add,
|
|
244
|
+
eq(s1).to(p1.expr),
|
|
245
|
+
eq(s2).to(p2.expr),
|
|
246
|
+
).then(
|
|
247
|
+
set_(p.expr).to(join(s1, s2)),
|
|
248
|
+
)
|
|
249
|
+
# Set p statements to join and next sym to p2 if both parents set
|
|
250
|
+
yield rule(
|
|
251
|
+
program_add,
|
|
252
|
+
eq(p1.parent).to(p),
|
|
253
|
+
eq(p2.parent).to(p),
|
|
254
|
+
eq(s1).to(p1.statements),
|
|
255
|
+
eq(s2).to(p2.statements),
|
|
256
|
+
eq(i).to(p2.next_sym),
|
|
257
|
+
).then(
|
|
258
|
+
set_(p.statements).to(join(s1, s2)),
|
|
259
|
+
set_(p.next_sym).to(i),
|
|
260
|
+
)
|
|
261
|
+
# Set p statements to empty and next sym to i if neither parents set
|
|
262
|
+
yield rule(
|
|
263
|
+
program_add,
|
|
264
|
+
p.compile(i),
|
|
265
|
+
ne(p1.parent).to(p),
|
|
266
|
+
ne(p2.parent).to(p),
|
|
267
|
+
).then(
|
|
268
|
+
set_(p.statements).to(String("")),
|
|
269
|
+
set_(p.next_sym).to(i),
|
|
270
|
+
)
|
|
271
|
+
# Set p statements to p1 and next sym to p1 if p1 parent set and p2 parent not set
|
|
272
|
+
yield rule(
|
|
273
|
+
program_add,
|
|
274
|
+
eq(p1.parent).to(p),
|
|
275
|
+
ne(p2.parent).to(p),
|
|
276
|
+
eq(s1).to(p1.statements),
|
|
277
|
+
eq(i).to(p1.next_sym),
|
|
278
|
+
).then(
|
|
279
|
+
set_(p.statements).to(s1),
|
|
280
|
+
set_(p.next_sym).to(i),
|
|
281
|
+
)
|
|
282
|
+
# Set p statements to p2 and next sym to p2 if p2 parent set and p1 parent not set
|
|
283
|
+
yield rule(
|
|
284
|
+
program_add,
|
|
285
|
+
eq(p2.parent).to(p),
|
|
286
|
+
ne(p1.parent).to(p),
|
|
287
|
+
eq(s2).to(p2.statements),
|
|
288
|
+
eq(i).to(p2.next_sym),
|
|
289
|
+
).then(
|
|
290
|
+
set_(p.statements).to(s2),
|
|
291
|
+
set_(p.next_sym).to(i),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
##
|
|
295
|
+
# Assign
|
|
296
|
+
##
|
|
297
|
+
|
|
298
|
+
# Compiling an assign is the same as compiling the expression, adding an assign statement, then setting the
|
|
299
|
+
# expression as the gensym, and setting is_identifier to true
|
|
300
|
+
program_assign = eq(p).to(p1.assign())
|
|
301
|
+
# Set parent
|
|
302
|
+
yield rule(program_assign, p.compile(i)).then(set_(p1.parent).to(p), set_(p.is_identifer).to(Bool(True)))
|
|
303
|
+
# If parent set, compile the expression
|
|
304
|
+
yield rule(program_assign, p.compile(i), eq(p1.parent).to(p)).then(p1.compile(i))
|
|
305
|
+
|
|
306
|
+
# 1. If p1 is not an identifier, then we must create a new one
|
|
307
|
+
|
|
308
|
+
# 1. a. If p1 parent is p, then use statements of p, next sym of p
|
|
309
|
+
symbol = join(String("_"), i.to_string())
|
|
310
|
+
yield rule(
|
|
311
|
+
program_assign,
|
|
312
|
+
eq(p1.parent).to(p),
|
|
313
|
+
eq(s1).to(p1.statements),
|
|
314
|
+
eq(i).to(p1.next_sym),
|
|
315
|
+
eq(s2).to(p1.expr),
|
|
316
|
+
eq(p1.is_identifer).to(Bool(False)),
|
|
317
|
+
).then(
|
|
318
|
+
set_(p.statements).to(join(s1, symbol, " = ", s2, "\n")),
|
|
319
|
+
set_(p.expr).to(symbol),
|
|
320
|
+
set_(p.next_sym).to(i + 1),
|
|
321
|
+
)
|
|
322
|
+
# 1. b. If p1 parent is not p, then just use assign as statement, next sym of i
|
|
323
|
+
yield rule(
|
|
324
|
+
program_assign,
|
|
325
|
+
ne(p1.parent).to(p),
|
|
326
|
+
p.compile(i),
|
|
327
|
+
eq(s2).to(p1.expr),
|
|
328
|
+
eq(p1.is_identifer).to(Bool(False)),
|
|
329
|
+
).then(
|
|
330
|
+
set_(p.statements).to(join(symbol, " = ", s2, "\n")),
|
|
331
|
+
set_(p.expr).to(symbol),
|
|
332
|
+
set_(p.next_sym).to(i + 1),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# 2. If p1 is an identifier, then program assign is a no-op
|
|
336
|
+
|
|
337
|
+
# 1. a. If p1 parent is p, then use statements of p, next sym of p
|
|
338
|
+
yield rule(
|
|
339
|
+
program_assign,
|
|
340
|
+
eq(p1.parent).to(p),
|
|
341
|
+
eq(s1).to(p1.statements),
|
|
342
|
+
eq(i).to(p1.next_sym),
|
|
343
|
+
eq(s2).to(p1.expr),
|
|
344
|
+
eq(p1.is_identifer).to(Bool(True)),
|
|
345
|
+
).then(
|
|
346
|
+
set_(p.statements).to(s1),
|
|
347
|
+
set_(p.expr).to(s2),
|
|
348
|
+
set_(p.next_sym).to(i),
|
|
349
|
+
)
|
|
350
|
+
# 1. b. If p1 parent is not p, then just use assign as statement, next sym of i
|
|
351
|
+
yield rule(
|
|
352
|
+
program_assign,
|
|
353
|
+
ne(p1.parent).to(p),
|
|
354
|
+
p.compile(i),
|
|
355
|
+
eq(s2).to(p1.expr),
|
|
356
|
+
eq(p1.is_identifer).to(Bool(True)),
|
|
357
|
+
).then(
|
|
358
|
+
set_(p.statements).to(String("")),
|
|
359
|
+
set_(p.expr).to(s2),
|
|
360
|
+
set_(p.next_sym).to(i),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
##
|
|
364
|
+
# Function two
|
|
365
|
+
##
|
|
366
|
+
|
|
367
|
+
# When compiling a function, the two args, p2 and p3, should get compiled when we compile p1, and should just be vars.
|
|
368
|
+
fn_two = eq(p).to(p1.function_two(p2, p3, s1))
|
|
369
|
+
# 1. Set parents of both args to p and compile them
|
|
370
|
+
# Assumes that this if the first thing to compile, so no need to check, and assumes that compiling args doesn't result in any
|
|
371
|
+
# change in the next sym
|
|
372
|
+
yield rule(fn_two, p.compile(i)).then(
|
|
373
|
+
set_(p2.parent).to(p),
|
|
374
|
+
set_(p3.parent).to(p),
|
|
375
|
+
set_(p1.parent).to(p),
|
|
376
|
+
p2.compile(i),
|
|
377
|
+
p3.compile(i),
|
|
378
|
+
p1.compile(i),
|
|
379
|
+
set_(p.is_identifer).to(Bool(True)),
|
|
380
|
+
)
|
|
381
|
+
# 2. Set statements to function body and the next sym to i
|
|
382
|
+
yield rule(
|
|
383
|
+
fn_two,
|
|
384
|
+
p.compile(i),
|
|
385
|
+
eq(s2).to(p1.expr),
|
|
386
|
+
eq(s3).to(p1.statements),
|
|
387
|
+
eq(s4).to(p2.expr),
|
|
388
|
+
eq(s5).to(p3.expr),
|
|
389
|
+
).then(
|
|
390
|
+
set_(p.statements).to(
|
|
391
|
+
join("def ", s1, "(", s4, ", ", s5, "):\n ", s3.replace("\n", "\n "), "return ", s2, "\n")
|
|
392
|
+
),
|
|
393
|
+
set_(p.next_sym).to(i),
|
|
394
|
+
set_(p.expr).to(s1),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
##
|
|
398
|
+
# Function three
|
|
399
|
+
##
|
|
400
|
+
|
|
401
|
+
fn_three = eq(p).to(p1.function_three(p2, p3, p4, s1))
|
|
402
|
+
yield rule(fn_three, p.compile(i)).then(
|
|
403
|
+
set_(p2.parent).to(p),
|
|
404
|
+
set_(p3.parent).to(p),
|
|
405
|
+
set_(p1.parent).to(p),
|
|
406
|
+
set_(p4.parent).to(p),
|
|
407
|
+
p2.compile(i),
|
|
408
|
+
p3.compile(i),
|
|
409
|
+
p1.compile(i),
|
|
410
|
+
p4.compile(i),
|
|
411
|
+
set_(p.is_identifer).to(Bool(True)),
|
|
412
|
+
)
|
|
413
|
+
yield rule(
|
|
414
|
+
fn_three,
|
|
415
|
+
p.compile(i),
|
|
416
|
+
eq(s2).to(p1.expr),
|
|
417
|
+
eq(s3).to(p1.statements),
|
|
418
|
+
eq(s4).to(p2.expr),
|
|
419
|
+
eq(s5).to(p3.expr),
|
|
420
|
+
eq(s6).to(p4.expr),
|
|
421
|
+
).then(
|
|
422
|
+
set_(p.statements).to(
|
|
423
|
+
join("def ", s1, "(", s4, ", ", s5, ", ", s6, "):\n ", s3.replace("\n", "\n "), "return ", s2, "\n")
|
|
424
|
+
),
|
|
425
|
+
set_(p.next_sym).to(i),
|
|
426
|
+
set_(p.expr).to(s1),
|
|
427
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# https://github.com/sklam/pyasir/blob/c363ff4f8f91177700ad4108dd5042b9b97d8289/pyasir/tests/test_fib.py
|
|
5
|
+
|
|
6
|
+
# In progress - should be able to re-create this
|
|
7
|
+
# @df.func
|
|
8
|
+
# def fib_ir(n: pyasir.Int64) -> pyasir.Int64:
|
|
9
|
+
# @df.switch(n <= 1)
|
|
10
|
+
# def swt(n):
|
|
11
|
+
# @df.case(1)
|
|
12
|
+
# def case0(n):
|
|
13
|
+
# return 1
|
|
14
|
+
|
|
15
|
+
# @df.case(0)
|
|
16
|
+
# def case1(n):
|
|
17
|
+
# return fib_ir(n - 1) + fib_ir(n - 2)
|
|
18
|
+
|
|
19
|
+
# yield case0
|
|
20
|
+
# yield case1
|
|
21
|
+
|
|
22
|
+
# r = swt(n)
|
|
23
|
+
# return r
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# With something like this:
|
|
27
|
+
# @egglog.function
|
|
28
|
+
# def fib(n: Int) -> Int:
|
|
29
|
+
# return (n <= Int(1)).if_int(
|
|
30
|
+
# Int(1),
|
|
31
|
+
# fib(n - Int(1)) + fib(n - Int(2)),
|
|
32
|
+
# )
|
egglog/ipython_magic.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from .bindings import EGraph
|
|
2
|
+
|
|
3
|
+
EGRAPH_VAR = "_MAGIC_EGRAPH"
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
get_ipython() # type: ignore[name-defined]
|
|
7
|
+
IN_IPYTHON = True
|
|
8
|
+
except NameError:
|
|
9
|
+
IN_IPYTHON = False
|
|
10
|
+
|
|
11
|
+
if IN_IPYTHON:
|
|
12
|
+
import graphviz
|
|
13
|
+
from IPython.core.magic import needs_local_scope, register_cell_magic
|
|
14
|
+
|
|
15
|
+
@needs_local_scope
|
|
16
|
+
@register_cell_magic
|
|
17
|
+
def egglog(line, cell, local_ns):
|
|
18
|
+
"""
|
|
19
|
+
Run an egglog program.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
|
|
23
|
+
%%egglog [output] [continue] [graph]
|
|
24
|
+
(egglog program)
|
|
25
|
+
|
|
26
|
+
If `output` is specified, the output of the program will be printed.
|
|
27
|
+
If `continue` is specified, the program will be run in the same EGraph as the previous cell.
|
|
28
|
+
If `graph` is specified, the EGraph will be displayed as a graph.
|
|
29
|
+
"""
|
|
30
|
+
if EGRAPH_VAR in local_ns and "continue" in line:
|
|
31
|
+
e = local_ns[EGRAPH_VAR]
|
|
32
|
+
else:
|
|
33
|
+
e = EGraph()
|
|
34
|
+
local_ns[EGRAPH_VAR] = e
|
|
35
|
+
cmds = e.parse_program(cell)
|
|
36
|
+
res = e.run_program(*cmds)
|
|
37
|
+
if "output" in line:
|
|
38
|
+
print("\n".join(res))
|
|
39
|
+
if "graph" in line:
|
|
40
|
+
return graphviz.Source(e.to_graphviz_string())
|
|
41
|
+
return None
|