使用序言显示布尔逻辑失败的原因

时间:2019-03-12 19:40:49

标签: prolog theorem-proving

假设我具有以下布尔逻辑:

Z = (A or B) and (A or C)

是否有可能使用prolog(可能与某些库一起使用)找出Z为何为假并以以下格式返回答案:

  1. Z为假,因为A或(b和c)为假
  2. 如果我用(c = true)代替一些已知值(或全部),它将说:Z为假,因为A为假
  3. 它可以告诉我哪个规则或规则的哪个部分导致此问题:Z为假,因为在“ Z =(A或B)和(A或C)”的(A或B)中A为假? li>

再次询问Z = true时一切都相反。

还是这类问题不适合序言,我应该看一下SAT解算器或其他一些东西?

我的目标是分析程序的数据流并确定答案之类的问题。我希望这是对/错,但事实并非如此,

1 个答案:

答案 0 :(得分:3)

我不得不说这是一个很酷的问题。

我看到了两种基本方法,一种是您使用Prolog的read_term/2来获得一个术语以及该术语中使用的变量名,看起来像这样:

?- read_term(T, [variable_names(Names)]).
|: X and Y or Z and Q.

T = _5700 and _5702 or _5706 and _5708,
Names = ['X'=_5700, 'Y'=_5702, 'Z'=_5706, 'Q'=_5708].

给予一些思考,这似乎比给我带来的麻烦要大得多,所以我想我将说明一个“简单”版本,其中我们实际上没有变量,只有原子和一个单独的变量变量值列表。您可能无需大量工作就可以使下面的一个适应上面的一个,但这似乎并没有解决这个问题的必要。

首先,我们需要支持这些运营商:

:- op(500, yfx, or).
:- op(400, yfx, and).

我继续前进,分别给它们与+和*相同的优先级,这对我来说似乎很直观。

我将有一个变量和赋值列表,例如[x=true, y=false]。我创建了一个谓词来进行管理,但是我认为最终会更好,因此将其删除,但是请注意,我们是在这里做出此决定的。

现在,我的计划是创建一个评估谓词,该谓词将采用表达式和变量值赋值。它会为提供的表达式产生一个布尔值,并产生一个“原因”。我将在此处作弊并使用“ is”运算符,但我这样做只是因为它对我来说很不错。最基本的术语是

evaluate(X, Assignments, Value, X is Value) :-
    atom(X), memberchk(X=Value, Assignments).

因此我们现在可以支持类似x的表达式:

?- evaluate(x, [x=true], V, R).
V = true,
R =  (x is true).

?- evaluate(x, [x=false], V, R).
V = false,
R =  (x is false).

这些有点像重言式;如果x = true,则表达式“ x”为true,因为x = true。假也是如此。但是我们在那里的原因是您所追求的。接下来,让我们处理“或”:

evaluate(X or _, Assignments, true, Reason) :-
    evaluate(X,  Assignments, true, Reason).
evaluate(_ or Y, Assignments, true, Reason) :-
    evaluate(Y,  Assignments, true, Reason).

因此,现在我们应该处理以下两种情况之一为真的情况:

?- evaluate(x or y, [x=true,y=false], V, R).
V = true,
R =  (x is true) ;
false.

?- evaluate(x or y, [x=false,y=true], V, R).
V = true,
R =  (y is true) ;
false.

如果给定x = true和y = true,我们将获得两种解决方案,一种用于x,另一种用于y。但是对于错误的情况,我们还需要一个规则:

evaluate(X or Y, Assignments, false, R1 and R2) :-
    evaluate(X,  Assignments, false, R1),
    evaluate(Y,  Assignments, false, R2).

因此,这里的想法是要注意“或”的两面均为假,然后将原因与“和”组合在一起。这样我们就可以得到:

?- evaluate(x or y, [x=false,y=false], V, R).
V = false,
R =  (x is false)and(y is false).

由于我们委派了双方,现在我们可以看到大型而复杂的“或”序列应该起作用:

?- evaluate(a or b or c or d or e, [a=false,b=false,c=false,d=true,e=false], V, R).
V = true,
R =  (d is true) ;
false.

您可以推断我们为“或”所做的工作是为“与”做的,这里我们基本上有两个错误的情况和一个真实的情况,而不是两个真实的情况和一个错误的情况:

evaluate(X and Y, Assignments, true, R1 and R2) :-
    evaluate(X,   Assignments, true, R1),
    evaluate(Y,   Assignments, true, R2).
evaluate(X and _, Assignments, false, Reason) :-
    evaluate(X,   Assignments, false, Reason).
evaluate(_ and Y, Assignments, false, Reason) :-
    evaluate(Y,   Assignments, false, Reason).

这适用于您可能期望的一些有趣的情况:

?- evaluate(x and y or z, [x=true, y=true, z=false], V, R).
V = true,
R =  (x is true)and(y is true) ;
false.

?- evaluate(x and y or z, [x=false, y=false, z=true], V, R).
V = true,
R =  (z is true) ;
false.

在某些情况下,这并不是超级有用,例如:

?- evaluate((a or b) and (a or c), [a=true,b=false,c=false], V, R).
V = true,
R =  (a is true)and(a is true) ;
false.

如您所见,第一个解决方案并不是超级有用的信息,因为我们已经讲了a;也许我们可以找到一种方法,通过更智能地组合孩子的答案来简化答案。另一方面,在这种情况下,它会有所帮助:

?- evaluate((a or b) and (a or c), [a=false,b=false,c=false], V, R).
V = false,
R =  (a is false)and(b is false) ;
V = false,
R =  (a is false)and(c is false).

?- evaluate((a or b) and (a or c), [a=false,b=true,c=true], V, R).
V = true,
R =  (b is true)and(c is true) ;
false.

编辑:处理未定义的值

唯一需要更改以处理未定义值的部分是atom(X)分支,应将其替换为:

evaluate(X, Assignments, Value, X is Value) :-
    atom(X),
    (    memberchk(X=V, Assignments) ->
         Value = V
    ;    member(Value, [true,false])
    ).

a=false出现在绑定列表中时,将使用它。如果未出现,则会同时生成a=falsea=true。从完全通用的角度来看,这似乎涵盖了您的其他用例:

?- evaluate((a or b) and (a or c), [a=false], V, Reason).
V = true,
Reason =  (b is true)and(c is true) ;

V = false,
Reason =  (a is false)and(b is false) ;

V = false,
Reason =  (a is false)and(c is false).

您可以将搜索限制为产生您感兴趣的值的案例:

?- evaluate((a or b) and (a or c), [a=false], false, Reason).
Reason =  (a is false)and(b is false) ;
Reason =  (a is false)and(c is false).

当然,Prolog在这里并没有做任何特别聪明的事情。它不是试图倒退找出bc的值将导致错误的原因,它只是产生所有可能性并尝试将其评估与false统一。因此,您添加到表达式中的每个未定义变量都会使搜索空间增加一倍。如果这是您担心的效率低下,那么这可能不是您的理想解决方案(或者可能很好,您可以尝试一下,看看它是否可以忍受)。

如果您最关心的是性能,我认为您可能希望研究SAT解算器,尽管我不知道这些解算器是否能够为您的推理提供“理由”。