Prolog 公式化效果公理

Prolog 公式化效果公理,prolog,logic,artificial-intelligence,clpfd,axiom,Prolog,Logic,Artificial Intelligence,Clpfd,Axiom,如何正确编写空(b,t)操作的效果公理使用谓词包含(b,l,t)。如果桶b在时间t时容纳l升水,则谓词的计算结果为True 清空(b,t):在时间t完全清空铲斗b。转移的效果在时间t+1时可见 转移(b,b',t):将尽可能多的水从桶b转移到桶b',而不会在时间t开始时溢出。转移的效果在时间t+1时可见 桶1装满水,可容纳7升。铲斗2为空,可容纳3升。目标状态是b2含有1升水 我认为正确的解决办法是: to any b,t,l( empty(b,t) -> contains(b,l,t))

如何正确编写空(b,t)操作的效果公理使用谓词包含(b,l,t)。如果桶b在时间t时容纳l升水,则谓词的计算结果为True

清空(b,t):在时间t完全清空铲斗b。转移的效果在时间t+1时可见

转移(b,b',t):将尽可能多的水从桶b转移到桶b',而不会在时间t开始时溢出。转移的效果在时间t+1时可见

桶1装满水,可容纳7升。铲斗2为空,可容纳3升。目标状态是b2含有1升水

我认为正确的解决办法是:

to any b,t,l( empty(b,t) -> contains(b,l,t))

这是正确的还是我应该将升的数量设置为l=5,例如?

我认为答案是:

Empty(b,t) => Contains(b,0,t+1)

对于这个问题,不需要显式的时间,因此我们将历史表示为一个操作列表。另一方面,您需要明确表示系统的状态,即三个bucket的当前内容。原因是Prolog数据结构(即术语)一旦创建就无法更改。由于存在许多无意义的术语,因此最好首先通过
is_type/1
谓词定义数据类型。因为您将在某个时刻使用算术(当您将水从一个桶倒到另一个桶时),所以我将使用算术约束,而不是古老的
is/2
谓词

:- use_module(library(clpfd)).
首先,我们声明有3个桶,由原子b1、b2和b3表示:

is_bucket(b1).
is_bucket(b2).
is_bucket(b3).
然后我们需要定义我们的状态。我们只使用一个术语
bucket/3
,其中第一个参数的容量为b1,另外两个参数的容量也是b1

is_state(buckets(X,Y,Z)) :-
    % each bucket contains at least 0 liters
    [X,Y,Z] ins 0 .. sup.
并非所有容器都会变为负数,因此我们将其域设置为从零到(正)无穷大的范围

现在什么是动作?到目前为止,您描述了清空和倾倒:

is_action(empty(B)) :-
    is_bucket(B).
is_action(pour(From, To)) :-
    is_bucket(From),
    is_bucket(To).
要清空一个桶,我们只需要知道是哪一个。如果我们把水从一个倒到另一个,我们需要描述两者。因为我们已经有了一个描述bucket的谓词,所以我们可以将一个规则声明为“如果
From
To
是bucket,那么
pour(From,To)
是一个动作

现在我们需要解释一个动作如何改变一个状态。这是旧状态和新状态之间的关系,因为我们想知道发生了什么,还有历史

% initial state
state_goesto_action(buckets(7,5,3), buckets(7,5,3), []).
初始状态的转换不会改变任何内容,并且具有空的历史记录(
[]

这条规则可以理解为“如果我们有一个动作来自某个状态
\u S0
导致状态
bucket(X,Y,Z)
和一些
历史
,那么接下来我们可以执行
清空(b1)
动作,我们将到达状态
bucket(0,Y,Z)
“。换言之,状态已更新,操作已在历史记录中预先设置。对称规则适用于其他桶:

state_goesto_action(buckets(X,Y,Z), buckets(X,0,Z), [empty(b2) | History]) :-
    state_goesto_action(_S0, buckets(X,Y,Z), History).
state_goesto_action(buckets(X,Y,Z), buckets(X,Y,0), [empty(b3) | History]) :-
    state_goesto_action(_S0, buckets(X,Y,Z), History).
我们如何检查这是否合理?让我们看看长度为2的历史:

?- state_goesto_action(_,S1,[H1,H2]).
S1 = buckets(0, 3, 5),
H1 = H2, H2 = empty(b1) .
啊,很好,如果两个动作都是空的(b1),那么第一个bucket是空的,而其他bucket是未触及的。让我们看看进一步的解决方案:

S1 = buckets(0, 0, 5),
H1 = empty(b1),
H2 = empty(b2) ;

S1 = buckets(0, 3, 0),
H1 = empty(b1),
H2 = empty(b3) ;

S1 = buckets(0, 0, 5),
H1 = empty(b2),
H2 = empty(b1) ;

S1 = buckets(7, 0, 5),
H1 = H2, H2 = empty(b2) ;

S1 = buckets(7, 0, 0),
H1 = empty(b2),
H2 = empty(b3) ;

S1 = buckets(0, 3, 0),
H1 = empty(b3),
H2 = empty(b1) ;

S1 = buckets(7, 0, 0),
H1 = empty(b3),
H2 = empty(b2) ;

S1 = buckets(7, 3, 0),
H1 = H2, H2 = empty(b3).
看起来我们有了清空水桶的所有可能性(除此之外就没有了:-)。现在,您需要添加从一个桶倒到另一个桶的规则。祝你好运


(编辑:输入错误,第二条规则中的错误)

我留下了旧答案,因为它留下了一些需要思考的部分(问题是只实现空操作)。只是为了提供完整的实现:

:- use_module(library(clpfd)).

bucket_capacity(b1,7).
bucket_capacity(b2,3).
bucket_capacity(b3,5).

% projections to a single bucket
state_bucket_value(buckets(X, _, _),b1,X).
state_bucket_value(buckets(_, Y, _),b2,Y).
state_bucket_value(buckets(_, _, Z),b3,Z).

% state update relation by a single bucket
state_updated_bucket_value(buckets(_, Y, Z), buckets(X0, Y,  Z ), b1, X0).
state_updated_bucket_value(buckets(X, _, Z), buckets(X,  Y0, Z ), b2, Y0).
state_updated_bucket_value(buckets(X, Y, _), buckets(X,  Y,  Z0), b3, Z0).


% initial state
state_goesto_action(S0, S0, []) :-
    S0 = buckets(X,Y,Z),
    bucket_capacity(b1,X),
    bucket_capacity(b2,Y),
    bucket_capacity(b3,Z).
% state transition for emptying
state_goesto_action(S1, S2, [empty(Bucket) | History]) :-
    state_updated_bucket_value(S1, S2, Bucket, 0),
    state_goesto_action(_S0, S1, History).
% state transition for pouring
state_goesto_action(S1, S3, [pour(From,To) | History]) :-
    bucket_capacity(b1,Max),
    state_bucket_value(S1,From,X),
    state_bucket_value(S1,To,Y),
    From0 #= min(X+Y, Max),
    To0 #= max(X-Y, 0),
    state_updated_bucket_value(S1, S2, From, From0),
    state_updated_bucket_value(S2, S3, To, To0),
    state_goesto_action(_S0, S1, History).
为了找出答案,如果我们能找到一个正好有一升的桶,我们可以公正地列举所有历史:

?- length(L,_), state_bucket_value(S,_,1), state_goesto_action(_, S, L).
L = [pour(b1, b3), pour(b1, b2), empty(b1), pour(b1, b3)],
S = buckets(5, 0, 1) ;
L = [pour(b1, b3), pour(b1, b2), pour(b1, b1), pour(b1, b3)],
S = buckets(5, 0, 1) ;
L = [pour(b1, b3), pour(b1, b2), pour(b2, b1), empty(b1)],
S = buckets(7, 0, 1) ;
L = [pour(b1, b3), pour(b1, b2), pour(b2, b1), pour(b1, b1)],
[...].
检查谓词是否可逆:

?- L = [pour(b1, b3), pour(b1, b2), empty(b1), pour(b1, b3)], state_goesto_action(_, S, L).
L = [pour(b1, b3), pour(b1, b2), empty(b1), pour(b1, b3)],
S = buckets(5, 0, 1) ;
false.

编辑:删除域约束以加快计算速度(我们从固定状态开始,因此约束始终是固定的,可以在不添加标签的情况下传播)。

至少对于Prolog,您的问题没有意义。试着用一种编程语言将其形式化,以获得一条线索。也许你应该首先解释,b、t和l代表什么,以及规则应该做什么。假设t是一个时间点,b代表一个水桶,l是一个水量(以升为单位),您会说:“如果在某个时间点t,任何水桶都是空的,那么任何水桶都有任意数量的水”。但你的公理的一个例子是“在时间t,如果桶b是空的,那么桶b包含100升水。”这是一个矛盾。既然矛盾的公理可以证明任何事情,你就不应该使用它们。@capelical@lambda.xy.x更新了这个问题,以便更好地理解。这很有道理,你能解释一下你的立场吗?你用哪本书作为参考?
?- L = [pour(b1, b3), pour(b1, b2), empty(b1), pour(b1, b3)], state_goesto_action(_, S, L).
L = [pour(b1, b3), pour(b1, b2), empty(b1), pour(b1, b3)],
S = buckets(5, 0, 1) ;
false.