Skip to content

Commit 6b3aabc

Browse files
tmp
1 parent de2f0a8 commit 6b3aabc

2 files changed

Lines changed: 75 additions & 7 deletions

File tree

docs/reference/python-integration.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,71 @@ r = ruleset(
679679
egraph.saturate(r)
680680
```
681681

682+
## Debugging and Inspection
683+
684+
When a rewrite or rule causes an unexpected equality, these hooks are the fastest ways to
685+
figure out which rules fired and what the e-graph contains.
686+
687+
### Run reports and rule counts
688+
689+
`EGraph.run(...)` returns a `RunReport` that includes counts and timings per rule.
690+
691+
```{code-cell} python
692+
egraph = EGraph()
693+
egraph.register(Math(2) + Math(100))
694+
report = egraph.run(3, ruleset=ruleset(rewrite(Math(2) + Math(100)).to(Math(102))))
695+
696+
# How many times each rule matched in this run:
697+
report.num_matches_per_rule
698+
699+
# Total time spent searching/applying each rule:
700+
report.search_and_apply_time_per_rule
701+
```
702+
703+
You can also retrieve cumulative stats for the current e-graph:
704+
705+
```{code-cell} python
706+
egraph = EGraph()
707+
egraph.register(Math(1) + Math(2))
708+
egraph.run(2)
709+
stats = egraph.stats()
710+
stats.num_matches_per_rule
711+
```
712+
713+
### Serialize the e-graph
714+
715+
If you create the e-graph with `save_egglog_string=True`, you can dump the program
716+
sent to egglog for offline inspection or minimization:
717+
718+
```{code-cell} python
719+
egraph = EGraph(save_egglog_string=True)
720+
egraph.register(Math(1) + Math(2))
721+
egraph.run(2)
722+
egglog_program = egraph.as_egglog_string
723+
```
724+
725+
For structural inspection, the internal serializer can be used to produce JSON or
726+
Graphviz output. These are especially useful when an unsound rewrite merges
727+
unexpected e-classes.
728+
729+
```{code-cell} python
730+
egraph = EGraph()
731+
egraph.register(Math(1) + Math(2))
732+
egraph.run(2)
733+
serialized = egraph._serialize(split_primitive_outputs=True) # internal helper
734+
json_blob = serialized.to_json()
735+
dot = serialized.to_dot()
736+
```
737+
738+
### Common pitfalls (rule authoring)
739+
740+
- Primitive container sorts like `Vec[...]` should not be merged or unioned. Avoid
741+
using merge functions that combine Vec outputs, and prefer one-way `set_` rules.
742+
- Guard vector indexing rules with bounds checks (`0 <= k < vs.length()`) to avoid
743+
`vec-get failed` panics and unsound conclusions.
744+
- Be careful with rules that build terms with negative lengths (e.g., `length - 1`);
745+
ensure they only fire when the length is proven positive.
746+
682747
## Custom Cost Models
683748

684749
By default, when extracting from the e-graph, we use a simple cost model, that looks at the costs assigned to each

python/egglog/exp/array_api.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ def _tuple_int(
766766
yield rewrite(TupleInt.fn(i2, idx_fn)[i]).to(idx_fn(check_index(i, i2)))
767767

768768
yield rewrite(TupleInt(vs).length()).to(Int(vs.length()))
769-
yield rewrite(TupleInt(vs)[Int(k)]).to(vs[k])
769+
yield rewrite(TupleInt(vs)[Int(k)]).to(vs[k], k >= 0, k < vs.length())
770770

771771
yield rewrite(TupleInt.fn(Int(k), idx_fn), subsume=True).to(TupleInt(k.range().map(lambda i: idx_fn(Int(i)))))
772772

@@ -872,7 +872,7 @@ def _tuple_tuple_int(
872872
yield rewrite(TupleTupleInt.fn(i2, idx_fn)[i]).to(idx_fn(check_index(i, i2)))
873873

874874
yield rewrite(TupleTupleInt(vs).length()).to(Int(vs.length()))
875-
yield rewrite(TupleTupleInt(vs)[Int(k)]).to(vs[k])
875+
yield rewrite(TupleTupleInt(vs)[Int(k)]).to(vs[k], k >= 0, k < vs.length())
876876

877877
yield rewrite(TupleTupleInt.fn(Int(k), idx_fn), subsume=True).to(
878878
TupleTupleInt(k.range().map(lambda i: idx_fn(Int(i))))
@@ -1221,7 +1221,7 @@ def _tuple_value(
12211221
yield rewrite(TupleValue.fn(i2, idx_fn)[i]).to(idx_fn(check_index(i, i2)))
12221222

12231223
yield rewrite(TupleValue(vs).length()).to(Int(vs.length()))
1224-
yield rewrite(TupleValue(vs)[Int(k)]).to(vs[k])
1224+
yield rewrite(TupleValue(vs)[Int(k)]).to(vs[k], k >= 0, k < vs.length())
12251225

12261226
yield rewrite(TupleValue.fn(Int(k), idx_fn), subsume=True).to(TupleValue(k.range().map(lambda i: idx_fn(Int(i)))))
12271227

@@ -1266,7 +1266,7 @@ def _tuple_tuple_value(
12661266
yield rewrite(TupleTupleValue.fn(i2, idx_fn)[i]).to(idx_fn(check_index(i, i2)))
12671267

12681268
yield rewrite(TupleTupleValue(vs).length()).to(Int(vs.length()))
1269-
yield rewrite(TupleTupleValue(vs)[Int(k)]).to(vs[k])
1269+
yield rewrite(TupleTupleValue(vs)[Int(k)]).to(vs[k], k >= 0, k < vs.length())
12701270

12711271
yield rewrite(TupleTupleValue.fn(Int(k), idx_fn), subsume=True).to(
12721272
TupleTupleValue(k.range().map(lambda i: idx_fn(Int(i))))
@@ -1741,7 +1741,7 @@ def _tuple_ndarray(
17411741
yield rewrite(TupleNDArray.fn(i2, idx_fn)[i]).to(idx_fn(check_index(i, i2)))
17421742

17431743
yield rewrite(TupleNDArray(vs).length()).to(Int(vs.length()))
1744-
yield rewrite(TupleNDArray(vs)[Int(k)]).to(vs[k])
1744+
yield rewrite(TupleNDArray(vs)[Int(k)]).to(vs[k], k >= 0, k < vs.length())
17451745

17461746
yield rewrite(TupleNDArray.fn(Int(k), idx_fn), subsume=True).to(
17471747
TupleNDArray(k.range().map(lambda i: idx_fn(Int(i))))
@@ -2486,8 +2486,11 @@ def _get_current_egraph() -> EGraph:
24862486

24872487
def try_evaling(egraph: EGraph, schedule: Schedule, expr: Expr, prim_expr: BuiltinExpr) -> Any:
24882488
"""
2489-
Try evaling the expression that will result in a primitive expression being fill.
2490-
if it fails, display the egraph and raise an error.
2489+
Try evaluating an expression that should produce a primitive (e.g., Bool/i64).
2490+
If extraction fails, register the expr, run the schedule, and retry.
2491+
On egglog panics we dump the .egg program for debugging.
2492+
A common failure mode is that no rule ever sets the primitive output
2493+
(e.g., `Boolean.to_bool` / `Int.to_i64`), so extraction fails.
24912494
"""
24922495
try:
24932496
return egraph.extract(prim_expr).value # type: ignore[attr-defined]

0 commit comments

Comments
 (0)