Используйте пролог, чтобы показать причину сбоя логической логики - PullRequest
2 голосов
/ 12 марта 2019

Предположим, у меня есть следующая логическая логика:

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

Возможно ли использовать пролог (возможно, вместе с некоторой библиотекой), чтобы выяснить, почему Z ложно, и вернуть ответ в следующих форматах:

  1. Z ложно, потому что A или (b и c) ложно
  2. Если я подставлю некоторые известные значения (или все), например (c = true)) что он скажет: Z ложно, потому что A ложно
  3. Оно может сказать мне, какое правило или какая часть правила вызывает эту проблему: Z ложно, потому что A ложно в (A или B) of "Z = (A или B) и (A или C)?

Снова те же вопросы, когда Z = истина со всем обратным.

Или такие вопросы не подходятдля пролога, и я должен взглянуть на какой-нибудь SAT-решатель или что-то еще?

Моя цель - проанализировать поток данных программы и вычислить ответы на такие вопросы, как:быть?

1 Ответ

3 голосов
/ 13 марта 2019

Я должен сказать, что это крутая проблема.

Я вижу два фундаментальных подхода, в которых вы используете пролог 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» является истинным, потому что 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=false, и a=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).

Конечно, Пролог здесь не делает ничего особенно умного; он не пытается работать в обратном направлении, чтобы выяснить, какие значения b и c приведут к ложному, он просто генерирует все возможности и пытается объединить их оценку с false. Таким образом, каждая неопределенная переменная, которую вы добавляете в выражение, удваивает пространство поиска. Если это неэффективность, о которой вы беспокоитесь, возможно, это не идеальное решение для вас (или, может быть, вам стоит попробовать и посмотреть, терпимо ли это).

Я думаю, что вы, возможно, захотите заглянуть в SAT-решатели, если ваша главная задача - производительность, хотя я не знаю, насколько они способны дать вам «причины» для своих выводов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...