簡體   English   中英

在Python中實現Prolog統一算法? 回溯

[英]Implementing the Prolog Unification algorithm in Python? Backtracking

我正在嘗試實施統一,但遇到問題......已經有很多例子,但他們所做的只是渾水。 我比開明更困惑:

http://www.cs.trincoll.edu/~ram/cpsc352/notes/unification.html

https://www.doc.ic.ac.uk/~sgc/teaching/pre2012/v231/lecture8.html [以下代碼基於此介紹]

http://www.cs.bham.ac.uk/research/projects/poplog/paradigms_lectures/lecture20.html#representing

https://norvig.com/unify-bug.pdf

如何在Java或C#等語言中實現統一算法?

Prolog的藝術......還有其他幾個。 最大的問題是我無法清楚地說明問題所在。 更多的骯臟或lispy解釋讓我更加困惑。

作為一個良好的開端,遵循基於列表的表示似乎是個好主意(例如在lispy情況下),即:

pred(Var, val)  =becomes=> [pred, Var, val] 
p1(val1, p2(val2, Var1)) ==> [p1, val1, [p2, val2, Var1]]

除了你如何表示自己的名單! 即[H | T]

我很樂意,如果你能告訴我一個Python偽代碼和/或更詳細的算法描述或指向一個。

我掌握的一些要點是需要在通用 - 統一和變量統一中分離代碼,但后來我無法看到相互背叛的情況! ... 等等。


作為旁注:我也很樂意提到你如何處理Backtracking上的統一。 我想我已經回歸平方,但我知道在回溯時替換幀會發生一些事情。


添加了當前代碼的答案。

http://www.igrok.site/bi/Bi_language.html

http://www.igrok.site/bi/TOC.html

https://github.com/vsraptor/bi/blob/master/lib/bi_engine.py

我將從“自動推理手冊”中快速總結Baader和Snyder關於統一理論的章節:

術語是從常量(以小寫字母開頭)和變量(以大寫字母開頭)構建的:

  • 沒有參數的常數是一個術語:例如car
  • 一個帶有術語作為參數的常量,一個所謂的函數應用程序,是一個術語。 例如date(1,10,2000)
  • 變量是一個術語,例如Date (變量永遠不會有參數)

替換是將術語分配給變量的映射。 在文獻中,這通常寫為{f(Y)/X, g(X)/Y}或帶有箭頭{X→f(Y), Y→g(X)} 將替換應用於術語會將每個變量替換為列表中的相應術語。 例如,上面應用於tuple(X,Y)的替換導致術語tuple(f(Y),g(X))

給定兩個項stunifier是使st相等的替換。 例如,如果我們將替換{a/X, a/Y}應用於期限date(X,1,2000) ,我們得到date(a,1,2000) ,如果我們將其應用到date(Y,1,2000)我們也得到date(a,1,2000) 換句話說,(句法)相等date(X,1,2000) = date(Y,1,2000)可以通過應用統一符{a/X,a/Y} 另一個更簡單的統一器是X/Y 最簡單的這種統一者稱為最通用的統一者 出於我們的目的,我們可以將自己局限於搜索這樣一個最通用的統一者,並且如果它存在,它就是唯一的(取決於某些變量的名稱)。

Mortelli和Montanari(參見文章的第2.2節和那里的參考文獻)給出了一組規則來計算這種最通用的統一者(如果存在的話)。 輸入是一組術語對(例如{f(X,b)= f(a,Y),X = Y})並且輸出是最通用的統一符(如果存在)或失敗(如果它不存在)。 在該示例中,替換{a / X,b / Y}將使第一對相等( f(a,b) = f(a,b) ),但是第二對將是不同的( a = b是不對)。

該算法不確定地從集合中選擇一個等式並對其應用以下規則之一:

  • 瑣碎:方程s = s (或X=X )已經相等,可以安全地去除。
  • 分解:等式f(u,v) = f(s,t)u=sv=t代替。
  • 符號沖突:等式a=bf(X) = g(X)終止進程失敗。
  • Orient: t=X形式的相等,其中t不是另一個變量被翻轉到X=t ,使得變量在左側。
  • 發生檢查:如果等式的形式為X=tt不是X本身,如果X出現在t某個地方,我們就會失敗。 [1]
  • 變量消除:我們有一個等式X=t ,其中X不出現在t ,我們可以將替換t/X應用於所有其他問題。

當沒有要應用的規則時,我們最終得到一組方程{X=s, Y=t, ...} ,表示要應用的替換。

以下是一些例子:

  • {f(a,X) = f(Y,b)}是可以統一的:分解得到{a = Y,X = b}並翻轉得到{Y = a,X = b}
  • {f(a,X,X) = f(a,a,b)}是不可統一的:分解得到{a = a,X = a,X = b},通過平凡消除a=a ,然后消除變量X得到{a=b}並且失敗並帶有符號沖突
  • {f(X,X) = f(Y,g(Y))}不可統一:分解得到{X=Y, X=g(Y)} ,消除變量X得到{Y=g(Y)} ,失敗並發生檢查

即使算法是非確定性的(因為我們需要選擇相等的工作),順序無關緊要。 因為您可以承諾任何訂單,所以無需撤消您的工作並嘗試使用其他等式。 這種技術通常稱為回溯,對於Prolog中的證明搜索是必需的,但對於統一本身則不然。

現在,您只需為術語和替換選擇合適的數據結構,並實現將替換應用於術語的算法以及基於規則的統一算法。

[1]如果我們試圖求解X = f(X) ,我們會看到X需要采用f(Y)形式來應用分解。 這導致解決問題f(Y) = f(f(Y))並且隨后Y = f(Y) 由於左側總是有一個f小於右側的應用,因此只要我們將術語看作有限結構,它們就不能相等。

我比開明更困惑

去過也做過。

注意:對於引用的任何源代碼,我沒有測試代碼並且不能說它是有效的,它們是作為示例給出的,並且看起來足夠正確,我將加載它們並針對它們運行測試用例以確定它們的有效性。

第一 :如果您使用正確的術語,使用反向鏈接而不是回溯,您將獲得更好的搜索結果。 例如, 后向鏈接/ inference.py

第二 :了解您的問題列出了三個單獨的階段。
1.統一算法
2.使用統一的后向鏈接
3.列表的數據結構。 您不會將其實現為Python源代碼,而是將其作為要傳遞給函數的文本。 見: 缺點

在進行反向鏈接之前,您應首先開發並完全測試統一。 然后在創建列表數據結構之前完全開發並測試反向鏈接。 然后完全測試您的列表數據結構。

第三 :實現統一算法的方法不止一種。
一個。 您注意到使用轉換規則的那個,或者Baader和Snyder在統一理論中記基於規則的方法 ,例如刪除 分解等。
我更喜歡在這個OCaml 示例或Python 示例中由Baader和Snyder通過統一理論中 的遞歸下降記錄為統一的算法
C。 我見過一些使用排列但目前找不到好的參考。

第四 :根據個人經驗,首先通過使用筆和紙來了解每個階段的工作原理,然后在代碼中實現它。

第五 :同樣從個人經驗來看,有很多關於如何做到這一點的信息,但是數學和技術論文可能會混淆許多對自學者至關重要的事物或者過於密集的事物。 我建議您專注於查找源代碼/數據結構的實現並使用它來學習。

第六 :將您的結果與實際工作代碼進行比較,例如SWI-Prolog

在進入下一階段之前,我不能強調你需要多少測試每個階段,並確保你擁有一套完整的測試用例。

當我想學習如何用函數式語言編寫這本書時,關於AI 1 2 3編程語言動物園的書籍是非常寶貴的。 不得不為LispOCaml安裝環境,但值得付出努力。

到目前為止,這適用於我提出的所有情況(除了一個需要進行檢查的情況,我尚未完成):

def unify_var(self, var, val, subst):
#   print "var> ", var, val, subst

    if var in subst :   
        return self.unify(subst[var], val, subst)
    elif isinstance(val, str) and val in subst : 
        return self.unify(var, subst[val], subst)
    #elif (var occurs anywhere in x) then return failure
    else :
        #print "%s := %s" % (var, val)
        subst[var] = val ; return subst

def unify(self, sym1, sym2, subst):
    #print 'unify>', sym1, sym2, subst

    if subst is False : return False
    #when both symbols match
    elif isinstance(sym1, str) and isinstance(sym2, str) and sym1 == sym2 : return subst
    #variable cases
    elif isinstance(sym1, str) and is_var(sym1) : return self.unify_var(sym1, sym2, subst)
    elif isinstance(sym2, str) and is_var(sym2) : return self.unify_var(sym2, sym1, subst)
    elif isinstance(sym1, tuple) and isinstance(sym2, tuple) : #predicate case
        if len(sym1) == 0 and len(sym2) == 0 : return subst
        #Functors of structures have to match
        if isinstance(sym1[0], str) and  isinstance(sym2[0],str) and not (is_var(sym1[0]) or is_var(sym2[0])) and sym1[0] != sym2[0] : return False
        return self.unify(sym1[1:],sym2[1:], self.unify(sym1[0], sym2[0], subst))
    elif isinstance(sym1, list) and isinstance(sym2, list) : #list-case
        if len(sym1) == 0 and len(sym2) == 0 : return subst
        return self.unify(sym1[1:],sym2[1:], self.unify(sym1[0], sym2[0], subst))

    else: return False

FAIL案件應該失敗:

OK: a <=> a : {}
OK: X <=> a : {'X': 'a'}
OK: ['a'] <=> ['a'] : {}
OK: ['X'] <=> ['a'] : {'X': 'a'}
OK: ['a'] <=> ['X'] : {'X': 'a'}
OK: ['X'] <=> ['X'] : {}
OK: ['X'] <=> ['Z'] : {'X': 'Z'}
OK: ['p', 'a'] <=> ['p', 'a'] : {}
OK: ['p', 'X'] <=> ['p', 'a'] : {'X': 'a'}
OK: ['p', 'X'] <=> ['p', 'X'] : {}
OK: ['p', 'X'] <=> ['p', 'Z'] : {'X': 'Z'}
OK: ['X', 'X'] <=> ['p', 'X'] : {'X': 'p'}
OK: ['p', 'X', 'Y'] <=> ['p', 'Y', 'X'] : {'X': 'Y'}
OK: ['p', 'X', 'Y', 'a'] <=> ['p', 'Y', 'X', 'X'] : {'Y': 'a', 'X': 'Y'}
 ================= STRUCT cases ===================
OK: ['e', 'X', ('p', 'a')] <=> ['e', 'Y', ('p', 'a')] : {'X': 'Y'}
OK: ['e', 'X', ('p', 'a')] <=> ['e', 'Y', ('p', 'Z')] : {'X': 'Y', 'Z': 'a'}
OK: ['e', 'X', ('p', 'a')] <=> ['e', 'Y', ('P', 'Z')] : {'X': 'Y', 'Z': 'a', 'P': 'p'}
OK: [('p', 'a', 'X')] <=> [('p', 'Y', 'b')] : {'Y': 'a', 'X': 'b'}
OK: ['X', 'Y'] <=> [('p', 'a'), 'X'] : {'Y': ('p', 'a'), 'X': ('p', 'a')}
OK: [('p', 'a')] <=> ['X'] : {'X': ('p', 'a')}
-----
FAIL: ['e', 'X', ('p1', 'a')] <=> ['e', 'Y', ('p2', 'Z')] : False
FAIL: ['e', 'X', ('p1', 'a')] <=> ['e', 'Y', ('p1', 'b')] : False
FAIL: [('p', 'a', 'X', 'X')] <=> [('p', 'a', 'a', 'b')] : False
(should fail, occurs) OK: [('p1', 'X', 'X')] <=> [('p1', 'Y', ('p2', 'Y'))] : {'Y': ('p2', 'Y'), 'X': 'Y'}
================= LIST cases ===================
OK: ['e', 'X', ['e', 'a']] <=> ['e', 'Y', ['e', 'a']] : {'X': 'Y'}
OK: ['e', 'X', ['a', 'a']] <=> ['e', 'Y', ['a', 'Z']] : {'X': 'Y', 'Z': 'a'}
OK: ['e', 'X', ['e', 'a']] <=> ['e', 'Y', ['E', 'Z']] : {'X': 'Y', 'Z': 'a', 'E': 'e'}
OK: ['e', 'X', ['e1', 'a']] <=> ['e', 'Y', ['e1', 'a']] : {'X': 'Y'}
OK: [['e', 'a']] <=> ['X'] : {'X': ['e', 'a']}
OK: ['X'] <=> [['e', 'a']] : {'X': ['e', 'a']}
================= FAIL cases ===================
FAIL: ['a'] <=> ['b'] : False
FAIL: ['p', 'a'] <=> ['p', 'b'] : False
FAIL: ['X', 'X'] <=> ['p', 'b'] : False

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM