对称二元谓词的基本一阶逻辑推理失败

时间:2017-04-19 23:34:13

标签: nlp artificial-intelligence nltk theorem-proving first-order-logic

超级基本问题。我试图表达两个二元谓词(父和子)之间的对称关系。但是,通过以下声明,我的解决方案证明允许我证明任何事情。转换后的CNF形式对我来说和分辨率证明一样有意义,但这应该是一个明显的错误案例。我错过了什么?

forall x,y (is-parent-of(x,y) <-> is-child-of(y,x)) 

我正在使用nltk python库和ResolutionProver证明器。这是nltk代码:

from nltk.sem import Expression as exp
from nltk.inference import ResolutionProver as prover

s = exp.fromstring('all x.(all y.(parentof(y, x) <-> childof(x, y)))')
q = exp.fromstring('foo(Bar)')
print prover().prove(q, [s], verbose=True)

输出:

[1] {-foo(Bar)}                             A 
[2] {-parentof(z9,z10), childof(z10,z9)}    A 
[3] {parentof(z11,z12), -childof(z12,z11)}  A 
[4] {}                                      (2, 3) 

True

1 个答案:

答案 0 :(得分:2)

以下是ResolutionProver的快速修复。

导致证明者不健全的问题是,当存在多个补充文字时,它不能正确实现解析规则。例如。如果条款{-A -B D}{A -A C D}二进制解析将产生子句{B -B C D}{C D}。两者都将作为重言式丢弃。相反,当前的NLTK实现会产生{A A}

这可能是因为条款在NLTK中表示为列表,因此在子句中可能会出现多次相同的文字。当应用于子句{-A -A}import nltk.inference.resolution as res def _simplify(clause): """ Remove duplicate literals from a clause """ duplicates=[] for i,c in enumerate(clause): if i in duplicates: continue for j,d in enumerate(clause[i+1:],start=i+1): if j in duplicates: continue if c == d: duplicates.append(j) result=[] for i,c in enumerate(clause): if not i in duplicates: result.append(clause[i]) return res.Clause(result) 时,此规则会正确生成空子句,但通常此规则不正确。

似乎如果我们让条款不再重复相同的文字,我们可以通过一些变化重新获得健全。

首先定义一个删除相同文字的函数。

以下是这种功能的简单实现

nltk.inference.resolution

现在我们可以将此函数插入​​def _iterate_first_fix(first, second, bindings, used, skipped, finalize_method, debug): """ This method facilitates movement through the terms of 'self' """ debug.line('unify(%s,%s) %s'%(first, second, bindings)) if not len(first) or not len(second): #if no more recursions can be performed return finalize_method(first, second, bindings, used, skipped, debug) else: #explore this 'self' atom result = res._iterate_second(first, second, bindings, used, skipped, finalize_method, debug+1) #skip this possible 'self' atom newskipped = (skipped[0]+[first[0]], skipped[1]) result += res._iterate_first(first[1:], second, bindings, used, newskipped, finalize_method, debug+1) try: newbindings, newused, unused = res._unify_terms(first[0], second[0], bindings, used) #Unification found, so progress with this line of unification #put skipped and unused terms back into play for later unification. newfirst = first[1:] + skipped[0] + unused[0] newsecond = second[1:] + skipped[1] + unused[1] # We return immediately when `_unify_term()` is successful result += _simplify(finalize_method(newfirst,newsecond,newbindings,newused,([],[]),debug)) except res.BindingException: pass return result res._iterate_first=_iterate_first_fix 模块的一些函数中。

def _iterate_second_fix(first, second, bindings, used, skipped, finalize_method, debug):
    """
    This method facilitates movement through the terms of 'other'
    """
    debug.line('unify(%s,%s) %s'%(first, second, bindings))

    if not len(first) or not len(second): #if no more recursions can be performed
        return finalize_method(first, second, bindings, used, skipped, debug)
    else:
        #skip this possible pairing and move to the next
        newskipped = (skipped[0], skipped[1]+[second[0]])
        result = res._iterate_second(first, second[1:], bindings, used, newskipped, finalize_method, debug+1)

        try:
            newbindings, newused, unused = res._unify_terms(first[0], second[0], bindings, used)
            #Unification found, so progress with this line of unification
            #put skipped and unused terms back into play for later unification.
            newfirst = first[1:] + skipped[0] + unused[0]
            newsecond = second[1:] + skipped[1] + unused[1]

            # We return immediately when `_unify_term()` is successful
            result += _simplify(finalize_method(newfirst,newsecond,newbindings,newused,([],[]),debug))
        except res.BindingException:
            #the atoms could not be unified,
            pass

    return result
res._iterate_second=_iterate_second_fix

同样更新res._iterate_second

clausify()

最后,将我们的函数插入def clausify_simplify(expression): """ Skolemize, clausify, and standardize the variables apart. """ clause_list = [] for clause in res._clausify(res.skolemize(expression)): for free in clause.free(): if res.is_indvar(free.name): newvar = res.VariableExpression(res.unique_variable()) clause = clause.replace(free, newvar) clause_list.append(_simplify(clause)) return clause_list res.clausify=clausify_simplify 以确保输入无重复。

parentof/childof

应用这些更改后,证明者应运行标准测试,并正确处理print res.ResolutionProver().prove(q, [s], verbose=True) 关系。

[1] {-foo(Bar)}                                  A 
[2] {-parentof(z144,z143), childof(z143,z144)}   A 
[3] {parentof(z146,z145), -childof(z145,z146)}   A 
[4] {childof(z145,z146), -childof(z145,z146)}    (2, 3) Tautology
[5] {-parentof(z146,z145), parentof(z146,z145)}  (2, 3) Tautology
[6] {childof(z145,z146), -childof(z145,z146)}    (2, 3) Tautology

False

输出:

Clause

更新:实现正确性不是故事的结局。一个更有效的解决方案是将用于存储{% comment %} # # Change date order by adding '| reversed' # To sort by title or other variables use {% assign sorted_posts = category[1] | sort: 'title' %} # {% endcomment %} {% assign sorted_cats = site.categories | sort %} {% for category in sorted_cats %} {% assign sorted_posts = category[1] | reversed %} <h2 id="{{category[0] | uri_escape | downcase }}">{{category[0] | capitalize}}</H2> <ul> {% for post in sorted_posts %} <li><a href="{{ site.url }}{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a></li> {% endfor %} </ul> {% endfor %} 类中的文字的容器替换为基于内置的基于Python散列的集合的容器,但这似乎需要对证明器实现进行更彻底的返工,引入一些性能测试基础设施。