Skip to content

Commit

Permalink
Merge branch 'main' into saulshanabrook/issue18
Browse files Browse the repository at this point in the history
  • Loading branch information
saulshanabrook authored Nov 24, 2023
2 parents a1aca0a + e8bcca6 commit cbe6488
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/reference/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This package is in active development and we welcome contributions.

## Documentation

We use the [Diátaxis framework](https://diataxis.fr/) to organize our documentation. The "explaination" section has
We use the [Diátaxis framework](https://diataxis.fr/) to organize our documentation. The "explanation" section has
been renamed to "Blog" since most of the content there is more like a blog post than a reference manual. It uses
the [ABlog](https://ablog.readthedocs.io/en/stable/index.html#how-it-works) extension.

Expand Down
18 changes: 9 additions & 9 deletions docs/reference/egglog-translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ file_format: mystnb

# Translation to/from egglog

The high level bindings available at the top module (`egglog`) expose most of the functionality of the `egglog` text format. This guide exaplin how to translate between the two.
The high level bindings available at the top module (`egglog`) expose most of the functionality of the `egglog` text format. This guide explains how to translate between the two.

Any EGraph can also be converted to egglog with the `egraph.as_egglog_string` property, as long as it was created with `Egraph(save_egglog_string=True)`.

Expand All @@ -21,7 +21,7 @@ The currently unsupported features are:

The builtin types of Unit, String, Int, Map, and Rational are all exposed as Python classes.

These can be imported from `egglog` can be instiated using the class constructor from the equivalent Python type. Many of the functions on them are mapped to Python operators. For example, the `>>` operator is mapped to `__rshift__` so it can be used as `a >> b` in Python.
These can be imported from `egglog` can be instantiated using the class constructor from the equivalent Python type. Many of the functions on them are mapped to Python operators. For example, the `>>` operator is mapped to `__rshift__` so it can be used as `a >> b` in Python.

```{code-cell} python
from __future__ import annotations
Expand Down Expand Up @@ -87,7 +87,7 @@ class Math(Expr):

### Parameterized sorts

In egglog, the builtin `Map` sort can be paramterized with the key and value sorts. In Python, we can use the generic typing syntax to do the same:
In egglog, the builtin `Map` sort can be parameterized with the key and value sorts. In Python, we can use the generic typing syntax to do the same:

```{code-cell} python
# egg: (sort MyMap (Map i64 String))
Expand All @@ -97,7 +97,7 @@ MyMap = Map[i64, String]
MyMap.empty().insert(i64(1), String("one"))
```

Since the generic types in the `Map` sort as sepecified with the `Generic` class, all of the methods will be statically checked, to make sure the right key/value types are used.
Since the generic types in the `Map` sort as specified with the `Generic` class, all of the methods will be statically checked, to make sure the right key/value types are used.

This doesn't require any custom type analysis on our part, only using Python's built in annotations with generic types.

Expand All @@ -122,7 +122,7 @@ The `function` decorator supports a number of options as well, which can be pass
- `cost`: The cost of the function. By default, this is 1.
- `default`: A default value for the function. This must be the same type as the return type of the function.
- `merge`: A function to merge the results of the function. This must be a function that takes two arguments of the return type, the old and the new, and returns a single value of the return type.
- `on_merge`: A function to call when the function is merged. This must be a function that takes two arguments of the return type, the old and hte new, and a number of actions to take.
- `on_merge`: A function to call when the function is merged. This must be a function that takes two arguments of the return type, the old and the new, and a number of actions to take.

```{code-cell} python
# egg: (function foo () i64 :cost 10 :default 0 :merge (max old new))
Expand All @@ -135,7 +135,7 @@ The static types on the decorator preserve the type of the underlying function,

### Datatype functions

In egglog, the `(datatype ...)` command can also be used to declare functions. All of the functions declared in this block return the type of the declared datatype. Similarily, in Python, we can use the `@egraph.class_` decorator on a class to define a number of functions associated with that class. These
In egglog, the `(datatype ...)` command can also be used to declare functions. All of the functions declared in this block return the type of the declared datatype. Similarly, in Python, we can use the `@egraph.class_` decorator on a class to define a number of functions associated with that class. These
can be either instance methods (including any supported `__` method), class methods, or the `__init__` method. The return type of these functions is inferred from the return type of the function. Additionally, any supported keyword argument for the `@egraph.function` decorator can be used here as well, by using the `@egraph.method` decorator to add values.

Note that by default, the egg name for any method is the Python class name combined with the method name. This allows us to define two classes with the same method name, with different signatures, that map to different egglog functions.
Expand Down Expand Up @@ -257,7 +257,7 @@ egraph.register(delete(fib(0)))
egraph.register(union(Boolean.TRUE | FALSE).with_(Boolean.TRUE))
```

Similar to the `set` funciton, this uses a fluent API, so that we can verify the types statically.
Similar to the `set` function, this uses a fluent API, so that we can verify the types statically.

### Expr as an action

Expand All @@ -281,7 +281,7 @@ except BaseException as e:

## Defining Rules

To define rules in Python, we create a rule with the `rule(*facts).then(*actions) `(rule ...)` command in egglog.
To define rules in Python, we create a rule with the `rule(*facts).then(*actions) (rule ...)` command in egglog.

```{code-cell} python
# egg:
Expand Down Expand Up @@ -369,7 +369,7 @@ Facts can be passed after the timeout to only run until those facts are reached:
egraph.run(10000, eq(fib(7)).to(i64(13)))
```

Rulsets can be run as well, by calling the `run` method on them:
Rulesets can be run as well, by calling the `run` method on them:

```{code-cell} python
# egg: (run 10 :ruleset path)
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/high-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ file_format: mystnb

The top level module contains the high level API for using e-graphs in Python.

The high level API is not documented yet, becuase adding supporting for our custom objects requires [a custom AutoDoc extension](https://www.sphinx-doc.org/en/master/development/tutorials/autodoc_ext.html#autodoc-ext-tutorial).
The high level API is not documented yet, because adding supporting for our custom objects requires [a custom AutoDoc extension](https://www.sphinx-doc.org/en/master/development/tutorials/autodoc_ext.html#autodoc-ext-tutorial).

## Example

The high level API builds on the low level API and is designed to:

1. Statically type checks as much as possible with MyPy
1. Statically type check as much as possible with MyPy
2. Be concise to write
3. Feels "pythonic"
3. Feel "pythonic"

Here is the same example using the high level API:

Expand Down
29 changes: 14 additions & 15 deletions docs/reference/python-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,19 @@ Conversion from an int:
egraph.eval(PyObject.from_int(1))
```

We also support evaling arbitrary Python bode, given some locals and globals. This technically allows us to implement any Python method:
We also support evaluating arbitrary Python code, given some locals and globals. This technically allows us to implement any Python method:

```{code-cell} python
egraph.eval(py_eval("1 + 2"))
```

Execing Python code is also supported. In this case, the return value will be the updated globals dict, which will be copied first before using.
Executing Python code is also supported. In this case, the return value will be the updated globals dict, which will be copied first before using.

```{code-cell} python
egraph.eval(py_exec("x = 1 + 2"))
```

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.
Alongside this, we support a function `dict_update` method, which can allow you to combine some local egglog expressions alongside, say, the locals and globals of the Python code you are evaluating.

```{code-cell} python
# Need this from our globals()
Expand All @@ -105,10 +105,9 @@ assert egraph.eval(evalled) == 3

### Simpler Eval

Instead of using the above low level primitive for evaling, there is a higher level wrapper function, `eval_fn`.
Instead of using the above low level primitive for evaluating, there is a higher level wrapper function, `eval_fn`.

It takes in a Python function and converts it to a function of PyObjects, by using `py_eval`
under the hood.
It takes in a Python function and converts it to a function of PyObjects, by using `py_eval` under the hood.

The above code code be re-written like this:

Expand Down Expand Up @@ -148,14 +147,14 @@ Math(2) + Math(i64(30)) + Math.var(String("x"))

You can also specify a "cost" for a conversion, which will be used to determine which conversion to use when multiple are possible. For example `convert(i64, Math, 10)`.

Regstering a conversion from A to B will also register all transitively reachable conversions from A to B, so you can also use:
Registering a conversion from A to B will also register all transitively reachable conversions from A to B, so you can also use:

```{code-cell} python
Math(2) + 30 + "x"
```

If you want to have this work with the static type checker, you can define your own `Union` type, which MUST include
have the `Expr`` class as the first item in the union. For example, in this case you could then define:
the `Expr` class as the first item in the union. For example, in this case you could then define:

```{code-cell} python
from typing import Union
Expand Down Expand Up @@ -185,7 +184,7 @@ bar(b=2, a=1)

### Default arguments

Default argument values are also supported. They are not translated to egglog definition, which has no notion of optional values. Instead, they are added to the args when the functions is called.
Default argument values are also supported. They are not translated to egglog definition, which has no notion of optional values. Instead, they are added to args when the functions is called.

```{code-cell} python
# egg: (function bar (i64 i64) i64)
Expand All @@ -203,7 +202,7 @@ When defining a custom class, you are free to use any method names you like.

### Builtin Methods

Most of the Python special dunder methods are supported as well:
Most of the Python special dunder (= "double under") methods are supported as well:

- `__lt__`
- `__le__`
Expand Down Expand Up @@ -234,8 +233,8 @@ Most of the Python special dunder methods are supported as well:

Currently `__divmod__` is not supported, since it returns multiple results and `__ne__` will shadow the builtin `!=` egglog operator.

Also thse methods are currently used in the runtime class and cannot be overriden currently, although we could change this
if the need arrises:
Also these methods are currently used in the runtime class and cannot be overridden currently, although we could change this
if the need arises:

- `__getattr__`
- `__repr__`
Expand All @@ -247,7 +246,7 @@ if the need arrises:

### "Preserved" methods

You can use the the `@egraph.method(preserve=True)` decorator to mark a method as "preserved", meaning that calling it will actually execute the body of the function and a coresponding egglog function will not be created,
You can use the `@egraph.method(preserve=True)` decorator to mark a method as "preserved", meaning that calling it will actually execute the body of the function and a corresponding egglog function will not be created,

Normally, all methods defined on a egglog `Expr` will ignore their bodies and simply build an expression object based on the arguments.

Expand Down Expand Up @@ -294,7 +293,7 @@ if TRUE | FALSE:
print("True!")
```

Note that the following list of methods are only supported as "preserved" since thy have to return a specific Python object type:
Note that the following list of methods are only supported as "preserved" since they have to return a specific Python object type:

- `__bool__`
- `__len__`
Expand Down Expand Up @@ -353,7 +352,7 @@ converter(Int, Float, Float.from_int)
assert str(-1.0 + Int.var("x")) == "Float(-1.0) + Float.from_int(Int.var(\"x\"))"
```

For methods which allow returning `NotImplemented`, i.e. the comparision + binary math methods, we will also try upcasting both
For methods which allow returning `NotImplemented`, i.e. the comparison + binary math methods, we will also try upcasting both
types to the type which is lowest cost to convert both to.

For example, if you have `Float` and `Int` wrapper types and you have write the expr `-1.0 + Int.var("x")` you might want the result to be `Float(-1.0) + Float.from_int(Int.var("x"))`:
Expand Down

0 comments on commit cbe6488

Please sign in to comment.