用于经典命题逻辑的 Quine 算法的 Prolog 实现(在 Quine 的“逻辑方法”中)

Jos*_*set 10 haskell prolog propositional-calculus

我只知道一个证明者可以翻译 Quine 在他的《逻辑方法》一书中为经典命题逻辑给出的算法(Harvard University Press, 1982, ch. 1 sec. 5, pp. 33-40),这个证明者是在 Haskell 和它在这里: Haskell 中的 Quine 算法

我试图在Prolog中翻译Quine的算法,但直到现在我还没有成功。很遗憾,因为它是一种高效的算法,而且 Prolog 翻译会很有趣。我将描述这个算法。我在开始时给出的唯一 Prolog 代码是对测试证明者有用的运算符列表:

% operator definitions (TPTP syntax)

:- op( 500, fy, ~).      % negation
:- op(1000, xfy, &).     % conjunction
:- op(1100, xfy, '|').   % disjunction
:- op(1110, xfy, =>).    % conditional
:- op(1120, xfy, <=>).   % biconditional
Run Code Online (Sandbox Code Playgroud)

真值常量分别为topbot,分别为truefalse。该算法开始如下:对于任何命题公式˚F,使这两个副本和替换,其具有最高发生的原子˚F通过top在所述第一拷贝,通过bot在第二个副本,然后应用以下十个减少规则之一对于每个副本,一次进行尽可能多的规则:

 1.  p & bot  --> bot
 2.  p & top  --> p
 3.  p | bot  --> p
 4.  p | top  --> top
 5.  p => bot --> ~p 
 6.  p => top --> top
 7.  bot => p --> top
 8.  top => p -->  p
 9.  p <=> bot --> ~p
 10. p <=> top --> p
Run Code Online (Sandbox Code Playgroud)

当然,我们对于否定和双重否定也有以下规则:

 1. ~bot --> top
 2. ~top --> bot
 3. ~~p  --> p 
Run Code Online (Sandbox Code Playgroud)

当既不存在top也没有bot在该式中所以没有的规则适用,再次分裂,并选择一个原子通过替换它top 通过bot在另一个双面表。公式F被证明当且仅当算法top在所有副本中结束,否则无法证明。

例子:

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

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

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

            top                       top <=> top                bot <=> bot

                                          top                        top
Run Code Online (Sandbox Code Playgroud)

很明显,Quine 的算法是对真值表方法的优化,但是从真值表生成器的程序代码开始,我没有成功地在 Prolog 代码中得到它。

欢迎至少在开始时提供帮助。提前,非常感谢。


Guy Coder 编辑

这是在 SWI-Prolog 论坛上重复发布的,该论坛进行了热烈的讨论,证明者 Prolog 已发布但未在此页面中复制。

Isa*_*bie 6

Haskell 代码对我来说似乎很复杂。这是基于问题中给出的算法描述的实现。(使用maplistdif来自 SWI-Prolog 库,但易于实现自包含。)

一、单一化简步骤:

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).
Run Code Online (Sandbox Code Playgroud)

然后,将这些步骤迭代应用到子项并在根处迭代,直到不再有任何变化:

formula_simple(Formula, Simple) :-
    Formula =.. [Operator | Args],
    maplist(formula_simple, Args, SimpleArgs),
    SimplerFormula =.. [Operator | SimpleArgs],
    (   formula_simpler(SimplerFormula, EvenSimplerFormula)
    ->  formula_simple(EvenSimplerFormula, Simple)
    ;   Simple = SimplerFormula ).
Run Code Online (Sandbox Code Playgroud)

例如:

?- formula_simple(~ ~ ~ ~ ~ a, Simple).
Simple = ~a.
Run Code Online (Sandbox Code Playgroud)

对于用其他值替换变量,首先是在公式中查找变量的谓词:

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).
Run Code Online (Sandbox Code Playgroud)

在回溯时,这将枚举公式中所有出现的变量,例如:

?- formula_variable((p => q) <=> (~q => ~p), Var).
Var = p ;
Var = q ;
Var = q ;
Var = p ;
false.
Run Code Online (Sandbox Code Playgroud)

这是下面证明过程中唯一的不确定性来源,您可以在formula_variable调用后插入一个剪切以提交单个选择。

现在实际替换 aVariable中的 aFormulaReplacement

variable_replacement_formula_replaced(Variable, Replacement, Variable, Replacement).
variable_replacement_formula_replaced(Variable, _Replacement, Formula, Formula) :-
    atom(Formula),
    dif(Formula, Variable).
variable_replacement_formula_replaced(Variable, Replacement, Formula, Replaced) :-
    Formula =.. [Operator | Args],
    Args = [_ | _],
    maplist(variable_replacement_formula_replaced(Variable, Replacement), Args, ReplacedArgs),
    Replaced =.. [Operator | ReplacedArgs].
Run Code Online (Sandbox Code Playgroud)

最后是证明者,构建一个类似于 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).
Run Code Online (Sandbox Code Playgroud)

问题示例的证明:

?- 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))) .
Run Code Online (Sandbox Code Playgroud)

它的所有证明:

?- 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))) ;
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))) ;
Proof = split((p=>q<=> ~q=> ~p), q, trivial((p=>top<=> ~top=> ~p)), split((p=>bot<=> ~bot=> ~p), p, trivial((top=>bot<=> ~bot=> ~top)), trivial((bot=>bot<=> ~bot=> ~bot)))) ;
Proof = split((p=>q<=> ~q=> ~p), q, trivial((p=>top<=> ~top=> ~p)), split((p=>bot<=> ~bot=> ~p), p, trivial((top=>bot<=> ~bot=> ~top)), trivial((bot=>bot<=> ~bot=> ~bot)))) ;
Proof = split((p=>q<=> ~q=> ~p), q, trivial((p=>top<=> ~top=> ~p)), split((p=>bot<=> ~bot=> ~p), p, trivial((top=>bot<=> ~bot=> ~top)), trivial((bot=>bot<=> ~bot=> ~bot)))) ;
Proof = split((p=>q<=> ~q=> ~p), q, trivial((p=>top<=> ~top=> ~p)), split((p=>bot<=> ~bot=> ~p), p, trivial((top=>bot<=> ~bot=> ~top)), trivial((bot=>bot<=> ~bot=> ~bot)))) ;
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))) ;
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))) ;
false.
Run Code Online (Sandbox Code Playgroud)

这包含很多冗余。同样,这是因为formula_variable枚举变量的出现。可以根据一个人的要求以各种方式使其更具确定性。

编辑:上面的实现formula_simple是幼稚且低效的:每次它在公式的根部进行成功的简化时,它也会重新访问所有子公式。但是在这个问题上,当根被简化时,子公式的新简化将变得不可能。这是一个新版本,它更小心地首先完全重写子公式,然后只在根处迭代重写:

formula_simple2(Formula, Simple) :-
    Formula =.. [Operator | Args],
    maplist(formula_simple2, Args, SimpleArgs),
    SimplerFormula =.. [Operator | SimpleArgs],
    formula_rootsimple(SimplerFormula, Simple).

formula_rootsimple(Formula, Simple) :-
    (   formula_simpler(Formula, Simpler)
    ->  formula_rootsimple(Simpler, Simple)
    ;   Simple = Formula ).
Run Code Online (Sandbox Code Playgroud)

这要快得多:

?- 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& ... & ...).
Run Code Online (Sandbox Code Playgroud)

编辑:正如评论中所指出的,上面写的证明者在稍微大一点的公式上可能会非常慢。问题是我忘记了一些运算符是可交换的!感谢jnmonette指出这一点。重写规则必须扩展一点:

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).
...
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为您需要在您的 Formula_simpler/2 谓词中添加一些子句:除了 ```formula_simpler(_P &amp; bot, bot).``` 之外,您还应该有 ```formula_simpler(bot &amp; _P, bot)。 ``` 对于所有使用交换运算符的子句都是一样的......大型公式的低效率可能来自那里...... (3认同)

jnm*_*tte 3

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

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)
    ).
Run Code Online (Sandbox Code Playgroud)

现在,您需要实现应用归约规则的reduce/2(在子公式中递归地),以及用top 或bot 不确定地替换公式的原子的branch/2,然后递归调用derive/2。就像是:

branch(Formula, D):-
    pickAtom(Formula, Atom),
    (
      Rep=top
    ; 
      Rep=bot
    ),
    replace(Formula, Atom, Rep, Formula1),
    derive(Formula1,D).
Run Code Online (Sandbox Code Playgroud)