Haskell Quine和x27的Prolog实现;经典命题逻辑的s算法(在奎因&“逻辑方法”中)

Haskell Quine和x27的Prolog实现;经典命题逻辑的s算法(在奎因&“逻辑方法”中),haskell,prolog,propositional-calculus,Haskell,Prolog,Propositional Calculus,我只知道一个证明者翻译了奎因在他的《逻辑方法》(哈佛大学出版社,1982年,第1章第5节,第33-40页)一书中为经典命题逻辑给出的算法,这个证明者在哈斯克尔,它在这里: 我试图用Prolog翻译奎因的算法,但直到现在我还没有成功。这是一个遗憾,因为它是一个有效的算法,Prolog翻译会很有趣。我将描述这个算法。我在开始时给出的唯一Prolog代码是用于测试验证程序的运算符列表: %运算符定义(TPTP语法) :-op(500,fy,~)。%否定 :-op(1000、xfy和)。%结合 :-o

我只知道一个证明者翻译了奎因在他的《逻辑方法》(哈佛大学出版社,1982年,第1章第5节,第33-40页)一书中为经典命题逻辑给出的算法,这个证明者在哈斯克尔,它在这里:

我试图用Prolog翻译奎因的算法,但直到现在我还没有成功。这是一个遗憾,因为它是一个有效的算法,Prolog翻译会很有趣。我将描述这个算法。我在开始时给出的唯一Prolog代码是用于测试验证程序的运算符列表:

%运算符定义(TPTP语法)
:-op(500,fy,~)。%否定
:-op(1000、xfy和)。%结合
:-op(1100,xfy,“|”)。%析取
:-op(1110,xfy,=>)。%有条件的
:-op(1120,xfy,年)双条件
真值常量分别为true和false的
top
bot
。该算法如下开始:对于任何命题公式F,复制两个副本,并将F中出现次数最多的原子替换为第一个副本中的
top
,以及第二个副本中的
bot
,然后对每一份副本应用以下十条规则,一次一条,尽可能多次:

1。p&bot-->机器人程序
2.p&top-->p
3.p | bot-->p
4.p |顶部-->顶部
5.p=>bot-->~p
6.p=>top-->top
7.bot=>p-->顶部
8.顶部=>p-->p
9p机器人-->~p
10p顶部-->p
当然,对于否定和双重否定,我们也有以下规则:

1~机器人-->顶部
2. ~顶部-->机器人
3. ~~p-->p
当公式中既没有
top
也没有
bot
,因此没有任何规则适用时,再次拆分它,并在另一个双面表中选择一个原子,用
top
bot
替换它。当且仅当算法在所有副本中以
top
结尾时,才证明公式F,否则无法证明

例如:

                         (p => q) <=> (~q => ~p)

 (p => top) <=> (bot => ~p)                 (p => bot) <=> (top => ~p)

       top  <=>  top                              ~p   <=>  ~p  

            top                       top <=> top                bot <=> bot

                                          top                        top
(p=>q)(~q=>~p)
(p=>top)(bot=>~p)(p=>bot)(top=>~p)
top-top~p~p
顶级机器人
顶端
很明显,Quine的算法是对真值表方法的优化,但从真值表生成器程序的代码开始,我没有成功地在Prolog代码中得到它

至少在开始的时候会得到帮助。首先,非常感谢


由Guy编码器编辑


这是SWI Prolog论坛上的两篇文章,该论坛有一个活跃的讨论,provers Prolog在该论坛上发布,但未在本页中复制。

这里是解决方案的框架。我希望它能帮你填补这些漏洞

is_valid(Formula) :-
    \+ derive(Formula,bot).

is_satisfiable(Formula) :-
    derive(Formula, top).

derive(bot,D):-
    !,
    D=bot.
derive(top,D):-
    !,
    D=top.
derive(Formula,D):-
    reduce(Formula, Formula1),
    (
      Formula=Formula1
    ->
      branch(Formula1,D)
    ;
      derive(Formula1,D)
    ).
现在需要实现reduce/2(在子公式中递归地应用约简规则),以及branch/2(用top或bot以非确定性方式替换公式的原子),然后递归地调用derivate/2。比如:

branch(Formula, D):-
    pickAtom(Formula, Atom),
    (
      Rep=top
    ; 
      Rep=bot
    ),
    replace(Formula, Atom, Rep, Formula1),
    derive(Formula1,D).

警告!我们不使用TPTP语法,而是使用来自SWI Prolog CLP(B)的语法。 似乎这种蛮力方法更古老(*),而且 由于Prolog代码太小,它甚至适合于 裤子口袋:

这是一个完整的实现。这个切口只被使用 以重写为主,并与之相当 哈斯克尔规则。除了Haskell可能没有 数据类型逻辑变量,如Prolog:

:- op(300, fy, ~).

eval(A, A) :- var(A), !.
eval(A+B, R) :- !, eval(A, X), eval(B, Y), simp(X+Y, R).
eval(A*B, R) :- !, eval(A, X), eval(B, Y), simp(X*Y, R).
eval(~A, R) :- !, eval(A, X), simp(~X, R).
eval(A, A).

simp(A, A) :- var(A), !.
simp(A+B, B) :- A == 0, !.
simp(A+B, A) :- B == 0, !.
simp(A+_, 1) :- A == 1, !.
simp(_+B, 1) :- B == 1, !.
simp(A*_, 0) :- A == 0, !.
simp(_*B, 0) :- B == 0, !.
simp(A*B, B) :- A == 1, !.
simp(A*B, A) :- B == 1, !.
simp(~A, 1) :- A == 0, !.
simp(~A, 0) :- A == 1, !.
simp(A, A).
代码不是纯Prolog,使用非逻辑var/1、(==)/2等。。元编程。像布尔一样,我们线性减少并执行两个替换的结合,因此我们在没有分支的情况下通过单个前沿进行奎因拆分:

judge(A, [B|R]) :- eval(A, B), 
                   term_variables(B, L), judge(B, L, R).

judge(_, [], R) :- !, R = [].
judge(A, [B|L], R) :-
  copy_term(A-[B|L], C-[0|L]),
  copy_term(A-[B|L], D-[1|L]), judge(C*D, R).
在上面,我们使用copy_term/2进行替换。这个想法是从Ulrich Neumerkels lambda图书馆借来的。我们还需要在eval/2和simp/2中提供=<和=:=。有关完整的源代码,请参阅。以下是您最喜欢的ISO序言中的示例运行:

?- judge(A+ ~A, L).
L = [A+ ~A, 1] /* Ends in 1, Tautology */

?- judge(A+ ~B, L).
L = [A+ ~B, ~B, 0] /* Ends in 0, Falsifiable */

?- judge(((P+Q)=<R)=:=((P=<R)*(Q=<R)), L).
L = [(P+Q =< R) =:= (P =< R)*(Q =< R),
  ((Q =< R) =:= (Q =< R))*(R =:= R*(Q =< R)),
  (R =:= R)*((R =:= R)*(R =:= R*R)), 1].  
?-判断(A+~A,L)。
L=[A+~A,1]/*以1结尾,重言式*/
-判断(A+~B,L)。
L=[A+~B,~B,0]/*以0结尾,可伪造*/

?-judge(((P+Q)=

哈斯克尔代码对我来说似乎很复杂。下面是一个基于问题中给出的算法描述的实现。(使用SWI Prolog库中的

maplist
dif
,但很容易实现自包含。)

首先,单一简化步骤:

formula_simpler(_P & bot,   bot).
formula_simpler(P & top,    P).
formula_simpler(P '|' bot,  P).
formula_simpler(_P '|' top, top).  % not P as in the question
formula_simpler(P => bot,   ~P).
formula_simpler(_P => top,  top).
formula_simpler(bot => _P,  top).
formula_simpler(top => P,   P).
formula_simpler(P <=> bot,  ~P).
formula_simpler(P <=> top,  P).
formula_simpler(~bot,       top).
formula_simpler(~top,       bot).
formula_simpler(~(~P),      P).
例如:

?- formula_simple(~ ~ ~ ~ ~ a, Simple).
Simple = ~a.
?- formula_variable((p => q) <=> (~q => ~p), Var).
Var = p ;
Var = q ;
Var = q ;
Var = p ;
false.
要用其他值替换变量,首先需要一个在公式中查找变量的谓词:

formula_variable(Variable, Variable) :-
    atom(Variable),
    dif(Variable, top),
    dif(Variable, bot).
formula_variable(Formula, Variable) :-
    Formula =.. [_Operator | Args],
    member(Arg, Args),
    formula_variable(Arg, Variable).
回溯时,这将枚举公式中所有变量的出现,例如:

?- formula_simple(~ ~ ~ ~ ~ a, Simple).
Simple = ~a.
?- formula_variable((p => q) <=> (~q => ~p), Var).
Var = p ;
Var = q ;
Var = q ;
Var = p ;
false.
最后是证明者,构造一个证明项,如Haskell版本:

formula_proof(Formula, trivial(Formula)) :-
    formula_simple(Formula, top).
formula_proof(Formula, split(Formula, Variable, TopProof, BotProof)) :-
    formula_simple(Formula, SimpleFormula),
    formula_variable(SimpleFormula, Variable),
    variable_replacement_formula_replaced(Variable, top, Formula, TopFormula),
    variable_replacement_formula_replaced(Variable, bot, Formula, BotFormula),
    formula_proof(TopFormula, TopProof),
    formula_proof(BotFormula, BotProof).
问题示例的证明:

?- formula_proof((p => q) <=> (~q => ~p), Proof).
Proof = split((p=>q<=> ~q=> ~p),
              p,
              split((top=>q<=> ~q=> ~top),
                    q,
                    trivial((top=>top<=> ~top=> ~top)),
                    trivial((top=>bot<=> ~bot=> ~top))),
              trivial((bot=>q<=> ~q=> ~bot))) .
这要快得多:

?- time(formula_simple(~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~(a & b & c & d & e & f & g & h & i & j & k & l & m & n & o & p & q & r & s & t & u & v & w & x & y & z), Simple)).
% 11,388 inferences, 0.004 CPU in 0.004 seconds (100% CPU, 2676814 Lips)
Simple = ~ (a&b&c&d&e&f&g&h& ... & ...).

?- time(formula_simple2(~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~(a & b & c & d & e & f & g & h & i & j & k & l & m & n & o & p & q & r & s & t & u & v & w & x & y & z), Simple)).
% 988 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 2274642 Lips)
Simple = ~ (a&b&c&d&e&f&g&h& ... & ...).
编辑:正如评论中所指出的,上面所写的验证程序在稍大的公式上可能会非常慢。问题是我忘记了一些运算符是可交换的!感谢您指出这一点。重写规则必须稍微扩展一下:

formula_simpler(_P & bot,   bot).
formula_simpler(bot & _P,   bot).
formula_simpler(P & top,    P).
formula_simpler(top & P,    P).
formula_simpler(P '|' bot,  P).
formula_simpler(bot '|' P,  P).
...

有了这一点,证明者表现得很好。

到目前为止你尝试了什么,在哪里遇到了困难?例如,你是否尝试过将所有规则写成序言从句,比如
mostCommonVar(公式,出现次数,命题值)
,或者
reduce(p&bot,Reduced):-reduce=reduce(p)。reduce(P&top,Reduced):-…
?谢谢你,乔恩!我不知道最常见的是什么,我会试试。我试过reduce,但我错过了一个很好的起点。我承认我输了。说服我为什么奎因的算法很重要,我会为此再投200分
?- time(formula_simple(~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~(a & b & c & d & e & f & g & h & i & j & k & l & m & n & o & p & q & r & s & t & u & v & w & x & y & z), Simple)).
% 11,388 inferences, 0.004 CPU in 0.004 seconds (100% CPU, 2676814 Lips)
Simple = ~ (a&b&c&d&e&f&g&h& ... & ...).

?- time(formula_simple2(~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~(a & b & c & d & e & f & g & h & i & j & k & l & m & n & o & p & q & r & s & t & u & v & w & x & y & z), Simple)).
% 988 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 2274642 Lips)
Simple = ~ (a&b&c&d&e&f&g&h& ... & ...).
formula_simpler(_P & bot,   bot).
formula_simpler(bot & _P,   bot).
formula_simpler(P & top,    P).
formula_simpler(top & P,    P).
formula_simpler(P '|' bot,  P).
formula_simpler(bot '|' P,  P).
...