Skip to content

Commit 9021953

Browse files
Add documentation for custom Python objects
1 parent 74b81e0 commit 9021953

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

docs/reference/python-integration.md

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,87 @@ file_format: mystnb
66

77
Alongside [the support for builtin `egglog` functionality](./egglog-translation.md), `egglog` also provides functionality to more easily integrate with the Python ecosystem.
88

9-
## Methods, Classmethods, and P
9+
## Custom Python Objects
10+
11+
We define a custom "primitive sort" (i.e. a builtin type) for `PyObject`s. This allows us to store any Python object in the e-graph.
12+
13+
### Saving Python Objects
14+
15+
To create an expression of type `PyObject`, we have to use the `egraph.save_object` method. This method takes a Python object and returns an expression of type `PyObject`.
16+
17+
```{code-cell} python
18+
from egglog import *
19+
egraph = EGraph()
20+
one = egraph.save_object(1)
21+
one
22+
```
23+
24+
We see that this as saved internally as a pointer to the Python object. For hashable objects like `int` we store two integers, a hash of the type and a has of the value.
25+
26+
We can also store unhashable objects in the e-graph like lists.
27+
28+
```{code-cell} python
29+
egraph.save_object([1, 2, 3])
30+
```
31+
32+
We see that this is stored with one number, simply the `id` of the object.
33+
34+
```{admonition} Mutable Objects
35+
:class: warning
36+
37+
While it is possible to store unhashable objects in the e-graph, you have to be careful defining any rules which create new unhashable objects. If each time a rule is run, it creates a new object, then the e-graph will never saturate.
38+
39+
Creating hashable objects is safer, since while the rule might create new Python objects each time it executes, they should have the same hash, i.e. be equal, so that the e-graph can saturate.
40+
```
41+
42+
### Retrieving Python Objects
43+
44+
The inverse of `egraph.save_object` is `egraph.load_object`. This takes an expression of type `PyObject` and returns the Python object it represents.
45+
46+
```{code-cell} python
47+
egraph.load_object(one)
48+
```
49+
50+
### Builtin methods
51+
52+
Currently, we only support a few methods on `PyObject`s, but we plan to add more in the future.
53+
54+
Conversion to/from a string:
55+
56+
```{code-cell} python
57+
egraph.extract(egraph.save_object('hi').to_string())
58+
```
59+
60+
```{code-cell} python
61+
egraph.load_object(egraph.extract(PyObject.from_string("1")))
62+
```
63+
64+
Conversion from an int:
65+
66+
```{code-cell} python
67+
egraph.load_object(egraph.extract(PyObject.from_int(1)))
68+
```
69+
70+
We also support evaling arbitrary Python bode, given some locals and globals. This technically allows us to implement any Python method:
71+
72+
```{code-cell} python
73+
empty_dict = egraph.save_object({})
74+
egraph.load_object(egraph.extract(py_eval("1 + 2", empty_dict, empty_dict)))
75+
```
76+
77+
Alongside this, we support a function `dict_update` method, which can allow you to combine some local local egglog expressions alongside, say, the locals and globals of the Python code you are evaling.
78+
79+
```{code-cell} python
80+
# Need this from our globals()
81+
def my_add(a, b):
82+
return a + b
83+
84+
locals_expr = egraph.save_object(locals())
85+
globals_expr = egraph.save_object(globals())
86+
# Need `one` to map to the expression for `1` not the Python object of the expression
87+
amended_globals = globals_expr.dict_update(PyObject.from_string("one"), one)
88+
evalled = py_eval("one + 2", locals_expr, amended_globals)
89+
assert egraph.load_object(egraph.extract(evalled)) == 3
90+
```
91+
92+
This is a bit subtle at the moment, and we plan on adding an easier wrapper to eval arbitrary Python code in the future.

0 commit comments

Comments
 (0)