Формулировка аксиомы эффекта

Как правильно писать аксиому эффекта для пустого (b, t) -действия с использованием предиката содержит (b, l, t) Предикат оценивает значение True, если ведро b удерживает l литров воды в момент времени t.

empty (b, t): полностью опустошает ведро b в момент времени t. Эффект переноса виден в момент времени 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, например?

Ответ 1

Для этой проблемы явное время не требуется, поэтому мы будем представлять историю как список действий. С другой стороны, вам нужно явно указать состояние вашей системы, то есть текущее содержимое трех ведер. Причина в том, что структуры данных Prolog (т.е. Термины) не могут быть изменены после их создания. Поскольку существует много бессмысленных терминов, рекомендуется сначала определить типы данных с помощью предиката is_type/1. Поскольку в какой-то момент вы будете использовать арифметику (когда вы выливаете воду из одного ведра в другой), я буду использовать арифметические ограничения вместо древнего предиката is/2.

:- use_module(library(clpfd)).

Сначала укажем, что есть 3 ведра, представленные атомами b1, b2 и b3:

is_bucket(b1).
is_bucket(b2).
is_bucket(b3).

Тогда нам нужно определить наше состояние. Мы просто используем термин buckets/3, где первый аргумент содержит емкость 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).

Чтобы освободить ведро, нам нужно только знать, какой из них. Если мы выливаем воду из одного в другой, нам нужно описать и то, и другое. Поскольку у нас уже есть предикат, описывающий ведро, мы можем просто указать правило как "Если From и To являются ведрами, то pour(From, To) является действием.

Теперь нам нужно объяснить, как действие трансформирует состояние. Это отношение между старым состоянием, новым состоянием и потому, что мы хотели бы знать, что происходит, а также историю.

% initial state
state_goesto_action(buckets(7,5,3), buckets(7,5,3), []).

Переход для исходного состояния ничего не меняет и имеет пустую историю ([]).

% state transitions for moving
state_goesto_action(buckets(X,Y,Z), buckets(0,Y,Z), [empty(b1) | History]) :-
    state_goesto_action(_S0, buckets(X,Y,Z), History).

Это правило можно прочитать как "Если бы у нас было действие, исходящее из некоторого состояния _S0, ведущего к состоянию buckets(X,Y,Z) с некоторым History, то мы можем выполнить действие empty(b1) next, и мы достигнем состояние buckets(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) .

Хорошо, если оба действия empty(b1), первое ведро пустое, а остальные нетронутые. Давайте рассмотрим дальнейшие решения:

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).

Похоже, у нас есть все возможности опорожнения ведер (и ничего более:-)). Теперь вам нужно добавить правила для заливки из одного ведра в другое. Удачи!

(Изменить: опечатки, ошибка во втором правиле)

Ответ 2

Я оставляю старый ответ, потому что он оставляет некоторые части, о которых нужно подумать (и речь идет только о реализации пустого действия). Просто чтобы обеспечить полную реализацию:

:- 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.

Изменить: Удаленные ограничения домена для ускорения вычислений (мы начинаем с фиксированного состояния, поэтому ограничения всегда будут заземлены и могут быть размножены без маркировки).

Ответ 3

Я думаю, что ответ будет следующим:

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