我一直在花一些时间仔细考虑电子图表。我觉得我有点折叠,直到它相当简单。这一实现可能不是高性能,因为主要诀窍是去除真正有用的索引结构。尽管如此,这种实施足够小,我可以把它留在我的脑海里。它变得非常可爱。
在计算机中,术语(a + b)* c通常被存储为树。 EGraph将此树结构转换为具有额外间接层的一个树结构。而不是直接连接到节点的节点,它们首先通过Integer标记的不同类型的节点连接。这些整数节点称为eMlasses,修改后的原始节点称为Enode。
Egraph是eMlasses和Enods之间的双链定向图。 Enodes属于eclasses。一旦我们开始合并eMlasses,emlasses将指向多个eNode。该图还还可以在无限术语的意义上开发允许表示的环。
上次https://www.philipzucker.com/union-find-dict/,我建立了一个Union的抽象 - 找到了dict。此数据结构允许您在仍然支持Union操作时检索键入等效类上的信息。鉴于这件作品,定义两个数据结构很简单。
eclass字段是一个Union-condice类从等效类到enodes的向量。我们告诉联盟的底层Intdisjointmap!在等价类中,我们将联合enode向量在Intdisjointmap的Codomain中彼此相互作用。
备忘录表不是严格必要的,但它给了我们一个很好的方法来查找eNode所属的eMlass。否则,我们必须蛮力搜索整个Intdisjointmap,以便在我们想要它们时找到enodes。
Enodes将引用符合Eclass ID,不幸的是可以陈旧。我们可以规范Enodes以使用最新鲜的等价类索引。
要将eNode添加到EGRAPT之前,我们可以致规定它,然后我们检查它是否已经在备忘录表中,如果不是我们实际上在IntDisjointMap中将其推动并更新备忘录表。
功能addende! (G :: EPRAGH,F :: ENODE)F = Canonize(g,f)如果haskey(g.备忘录,f)返回g。备忘录[f] else id =推! (g。emlass,[f])g。备忘录[f] = ID返回ID结束末端#Vonvenience函数用于推动julia expr addexpr! (g :: Egraph,f ::符号)= addende! (g,enode(f,[]))函数addexpr! (g :: egraph,f :: expr)@assert f。头==:致电addende! (g,eNode(f。args [1],[addexpr!(g,a)为f。args [2:end]]))结束
当我们向EGRAGRS致力于Egraph时,我们采取联盟!两个相应的偏蚀。我们联盟!然后,底层的IntdisjointMap,然后我们重新创建了该eMlass中的所有隐藏enodes并更新备忘录表。
函数base.union! (g :: Egraph,f :: int64,g :: int64)Id = Union! (g. eMlass,f,g)Eclass = eNode中的eNode []。 eclass [id]删除! (g。备忘录,eNode)#我们甚至应该打扰删除吗? eNode = canonize(g,enode)#应该典可返回它是否有什么事? G 。备忘录[eNode] = ID推动! (emlass,eNode)结束g。 eclass [id] = emlass结束
我们尚未讨论的大件事是计算同时关闭。在我的原始演示文稿中,这是一个整个磨难,而且我们需要将父指针从eNods维护eNodes.这非常令人困惑。
相反,我们可以通过蛮力扫过电子镜头来找到同时。与父指针有可能指向我们的候选人相比,这是效率低下。但是,在天真的映射期间,无论如何,我们都会在EGROGP上扫描,以找到可能的重写规则应用程序。这种方法使得在某种意义上使得与其他重写规则相当相似。可能存在一些实用性,而不是考虑一致性关闭作为Egraph的真正内在部分。也许你可以将它用于同时不严格持有的系统?
一个不幸的是,必须在最坏的情况下应用一致与Egraph的深度成比例的时间,因为它一次只传播一层一层。
它是如何运作的:在工会之后!操作在备忘录和eMlass中存在非典型enode。这些非Canonical enodes是那些具有刚刚转变为另一eClass的子项的参数的参数。这些也是一般的enodes,即用于同时闭合传播的候选者。我们可以在扫描期间通过典范来检测它们。
这一昂贵的同时扫过更多的罪,而不是更有效的罪。可能发生的事情是我们尝试addexpr!作为陈旧的忠实,换句话说,它应该在备忘录中但不是。这将错误地为此eNode创建一个新的eclass。但是,同时关闭扫描将在下一个传球上找到这种等价。
#我忘记在我的上一篇文章中包含此IntdisjointMap迭代器功能。 #概念上它属于那里。函数base.igerate(x :: intdisjointmap,state = 1),而状态< =长度(x。父母)如果x。父母[状态]< 0返回((陈述,x。值[状态]),状态+ 1)结束状态+ = 1结束返回NOLL END#返回发现的同时函数同时的组合列表(G :: EGROP)BUF = TOU {INT64, int64} [] for(id1,eclass)中的g。 Eclass #Alterniply迭代Enclass Cnode = Canonize(g,eNode)的enode备忘录,如果haskey(g.备忘录,cnode)ID2 = G.备忘录[CNODE]如果ID1!= ID2推! (BUF,(ID1,ID2))结束结束结束返回BUF端#传播所有同时函数传播_Congruence(G :: Egraph)cong =长度(cong)&gt的同时(g); 0 for(i,j)在Cong Union! (g,i,j)结束ing =同时(g)结束
原则上,我认为这种配方使得更容易并行化同一度伴随重写规则匹配。重写过程成为查找元组之间并实际应用它们之间的交换。
要添加分析,您希望在IntdisjointMap中存储复合结构。元组{vector {enode},分析)合并操作然后是eNode合并和分析合并。
可能更改eNode是二进制的可能很好。一个人可以编写任意的arity。然后一切都适合适当的数组,取消了重大的间接
使用Eggraph使用测试@testset"基本的电子版和#34; begin g = Egraph()a = addende! (g,eNode(:a,[]))b = addende! (g,enode(:b,[]))#println(g)联盟! (g,a,b)#println(g)@test addende! (g,eNode(:a,[]))== 2 @Test AddEnode! (g,eNode(:c,[]))== 3 @Test AddEnode! (g,eNode(:f,[a]))== 4联盟! (g,3,4)#= println(g)在g.eclass println(k,v)end =#g = egraph()a = addende! (g,eNode(:a,[]))b = addende! (g,eNode(:b,[]))c = addende! (g,eNode(:c,[]))联盟! (g,a,b)fa = addende! (g,eNode(:f,[a]))fb = addende! (g,eNode(:f,[b]))fc = addende! (g,eNode(:f,[c]))ffa = addende! (g,eNode(:f,[fa]))ffb = addende! (g,eNode(:f,[fb]))@test同时(g)== [(fb)](x,y)在同时(g)联盟! (g,x,y)结束@test同时(g)== [(ffa,ffb)]联盟! (g,a,c)@test同时(g)== [(ff,ffb),(ffa,ffb)for(x,y)在同时(g)联盟中! (g,x,y)结束@test同时(g)== [] g = egraph()f5a = addexpr! (g,:(f(f(f(f(f(a))))))))f2a = addexpr! (g,:(f(f(a)))))@test长度(g。eclass)== 6联盟! (g,f5a,f2a)@test find_root(g,f5a)== find_root(g,f2a)@test长度(g。eclass)== 5 f5a = addexpr! (g,:(f(f(f(f(f(a))))))))f2a = addexpr! (g,:(f(f(a))))))@test长度(g。eclass)== 5 g = egraph()f5a = addexpr! (g,:(f(f(f(f(a))))))))))fa = addexpr! (g,:(f(a)))a = addexpr! (g,:a)@test长度(g。eclass)== 6联盟! (g,fa,a)@test find_root(g,fa)== find_root(g,a)传播_congruence(g)@test长度(g。eclass)== 1 g = egraph()ffa = addexpr! (g,:(f(f(a)))))f5a = addexpr! (g,:(f(f(f(f(a)))))))a = addexpr! (g,:a)@test长度(g。eclass)== 6联盟! (g,ffa,a)@test find_root(g,ffa)== find_root(g,a)传播_congruence(g)@test长度(g。eclass)== 2结束