I believe I have found a bug related to egraph merging/rebuilding. It occurs in this test in basic.ml:
let%test "test" =
let g = EGraph.init () in
let c1 = EGraph.add_sexp g [%s 1] in
let c2 = EGraph.add_sexp g [%s 2] in
let f1 = EGraph.add_sexp g [%s (f 1)] in
let f2 = EGraph.add_sexp g [%s (f 2)] in
EGraph.merge g c1 c2;
EGraph.rebuild g;
let query = Query.of_sexp [%s f "?a"] in
let matches = EGraph.ematch g (EGraph.eclasses g) query |> Iter.length in
Alcotest.(check int) "check num matches" 1 matches
Here, we merge 1 and 2, so f 1 = f 2 by congruence. Thus the query f ?a should return exactly one match. However, two matches are found instead.
This behavior is the same before and after the patch I previously made, and I believe its cause is unrelated to the e-matching algorithm itself.
After some investigating, I determined that initially, there are four e-classes, which I will call c1, c2, f1, f2, which correspond to
the expressions 1, 2, f 1, and f 2, respectively. Since 1 and 2 are merged, the e-classes c1 and c2 are merged, so f1 and f2 should be merged as well. However, they are not merged, and so both f1 and f2 are treated as e-classes we should search, erroneously adding another match. This can be verified by checking that f1 and f2 do not have the same canonical e-class id after rebuilding.
I don't yet fully understand the problem, but it seems that the rebuilding is not propagating to the parent e-classes. In particular, I'm a bit confused about the e-graph's class_members field, which purportedly maps e-classes to its children e-nodes. From my understanding (see egg paper sec. 3.1), maintaining the congruence invariant requires upward merging, which would mean storing the parent e-nodes for each e-class instead. But I could be misunderstanding it.
I believe I have found a bug related to egraph merging/rebuilding. It occurs in this test in
basic.ml:Here, we merge
1and2, sof 1 = f 2by congruence. Thus the queryf ?ashould return exactly one match. However, two matches are found instead.This behavior is the same before and after the patch I previously made, and I believe its cause is unrelated to the e-matching algorithm itself.
After some investigating, I determined that initially, there are four e-classes, which I will call c1, c2, f1, f2, which correspond to
the expressions
1,2,f 1, andf 2, respectively. Since1and2are merged, the e-classesc1andc2are merged, sof1andf2should be merged as well. However, they are not merged, and so bothf1andf2are treated as e-classes we should search, erroneously adding another match. This can be verified by checking thatf1andf2do not have the same canonical e-class id after rebuilding.I don't yet fully understand the problem, but it seems that the rebuilding is not propagating to the parent e-classes. In particular, I'm a bit confused about the e-graph's
class_membersfield, which purportedly maps e-classes to its children e-nodes. From my understanding (see egg paper sec. 3.1), maintaining the congruence invariant requires upward merging, which would mean storing the parent e-nodes for each e-class instead. But I could be misunderstanding it.