Пролог SAT Solver

Я пытаюсь создать простой пролог SOL Sollog. Моя идея состоит в том, что пользователь должен ввести логическую формулу, которая будет решена в CNF (Conjuctive Normal Form) с использованием списков Prolog, например (A или B) и (B или C) должна быть представлена ​​как sat ([[A, B], [B, C]]), а Prolog позволяет найти значения для A, B, C.

Мой следующий код не работает, и я не понимаю, почему. На этой строке трассы Call: (7) sat ([[true, true]])? Я ожидал start_solve_clause ([_ G609, _G612]]).

Отказ от ответственности: Извините за дерьмовый код. Я даже не знал о Prolog или проблеме SAT несколько дней назад.

P.S.: Совет по разрешению SAT приветствуется.

Трассировка

sat([[X, Y, Z], [X, Y]]).
Call: (6) sat([[_G609, _G612, _G615], [_G609, _G612]]) ? creep
Call: (7) start_solve_clause([_G609, _G612, _G615]) ? creep
Call: (8) solve_clause([_G615], _G726) ? creep
Call: (9) or(_G725, _G615, true) ? creep
Exit: (9) or(true, true, true) ? creep
Exit: (8) solve_clause([true], true) ? creep
Call: (8) or(_G609, _G612, true) ? creep
Exit: (8) or(true, true, true) ? creep
Exit: (7) start_solve_clause([true, true, true]) ? creep
Call: (7) sat([[true, true]]) ?  

Источник пролога

% Knowledge base
or(true, true, true).
or(false, false, false).
or(true, false, true).
or(false, true, true).

or(not(true), true, true).
or(not(false), false, true).
or(not(true), false, false).
or(not(false), true, true).

or(true, not(true), true).
or(false, not(false), true).
or(true, not(false), true).
or(false, not(true), false).

% SAT solver
sat([]).
sat([Clause | Tail]) :- start_solve_clause(Clause), sat(Tail).

% Clause solver
start_solve_clause([Var1, Var2 | Tail]) :- solve_clause(Tail, Result), or(Var1, Var2, Result).

solve_clause([X | []], Result) :- or(Result, X, true).
solve_clause([X | Tail], Result) :- solve_clause(Tail, Result2), or(Result, X, Result2).

Ответ 1

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

sat(Stmt) :-
  call(Stmt).

а затем вы будете ссылаться на свой пример, выполнив (btw ; is или)

?- sat(((A ; B), (B ; C))).

возможно, вам нужно что-то, чтобы ограничить то, что они являются либо истинными, либо ложными, поэтому добавьте эти правила...

is_bool(true).
is_bool(false).

и запрос

?- is_bool(A), is_bool(B), is_bool(C), sat(((A ; B), (B ; C))).

BTW - это просто просто будет делать DFS, чтобы найти удовлетворяющие термины. нет умной эвристики или чего-то еще.

Ответ 2

Есть замечательная статья Хоу и Кинга о SAT Solving in (SICStus) Prolog (см. http://www.soi.city.ac.uk/~jacob/solver/index.html).

sat(Clauses, Vars) :- 
    problem_setup(Clauses), elim_var(Vars). 

elim_var([]). 
elim_var([Var | Vars]) :- 
    elim_var(Vars), (Var = true; Var = false). 

problem_setup([]). 
problem_setup([Clause | Clauses]) :- 
    clause_setup(Clause), 
    problem_setup(Clauses). 

clause_setup([Pol-Var | Pairs]) :- set_watch(Pairs, Var, Pol). 

set_watch([], Var, Pol) :- Var = Pol. 
set_watch([Pol2-Var2 | Pairs], Var1, Pol1):- 
    watch(Var1, Pol1, Var2, Pol2, Pairs). 

:- block watch(-, ?, -, ?, ?). 
watch(Var1, Pol1, Var2, Pol2, Pairs) :- 
    nonvar(Var1) -> 
        update_watch(Var1, Pol1, Var2, Pol2, Pairs); 
        update_watch(Var2, Pol2, Var1, Pol1, Pairs). 

update_watch(Var1, Pol1, Var2, Pol2, Pairs) :- 
    Var1 == Pol1 -> true; set_watch(Pairs, Var2, Pol2).

Предложения в CNF приведены в следующем виде:

| ?- sat([[true-X,false-Y],[false-X,false-Y],[true-X,true-Z]],[X,Y,Z]).
 X = true,
 Y = false,
 Z = true ? ;
 X = false,
 Y = false,
 Z = true ? ;
 X = true,
 Y = false,
 Z = false ? ;
no

Ответ 3

Можно использовать CLP (FD) для решения SAT. Просто начните с CNF и затем обратите внимание, что пункт:

x1 v .. v xn 

Может быть представлено как ограничение:

x1 + .. + xn #> 0

Далее для отрицательного литерала:

~x

Просто используйте:

1-x

Вам нужно ограничить переменные до домена 0..1 и вызывать маркировку. Как только маркировка вернет некоторые значение для переменных, вы знаете, что ваш оригинал формула является выполнимой.

Вот пример, запускающий тест Джо Лемана:

Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 6.5.2)
Copyright (c) 1990-2013 University of Amsterdam, VU Amsterdam

?- use_module(library(clpfd)).

?- L = [X,Y,Z], L ins 0..1, X+1-Y #> 0, 1-X+1-Y #> 0, X+Z #> 0, label(L).
X = Y, Y = 0,
Z = 1 ;
X = 1,
Y = Z, Z = 0 ;
X = Z, Z = 1,
Y = 0.

Bye

Логическое программирование ограничений по конечным доменам
http://www.swi-prolog.org/man/clpfd.html

Ответ 4

Иногда появляется следующее кодирование. Статьи представленный назначением отличного положительного отличного от нуля целые числа к пропозициональным переменным:

x1 v .. v xn --> [x1, .. , xn]
~x           --> -x

Кажется, что следующий код Prolog работает достаточно хорошо:

% mem(+Elem, +List)
mem(X, [X|_]).
mem(X, [_|Y]) :-
    mem(X, Y).

% sel(+Elem, +List, -List)
sel(X, [X|Y], Y).
sel(X, [Y|Z], [Y|T]) :-
    sel(X, Z, T).

% filter(+ListOfList, +Elem, +Elem, -ListOfList)
filter([], _, _, []).
filter([K|F], L, M, [J|G]) :-
    sel(M, K, J), !,
    J \== [],
    filter(F, L, M, G).
filter([K|F], L, M, G) :-
    mem(L, K), !,
    filter(F, L, M, G).
filter([K|F], L, M, [K|G]) :-
    filter(F, L, M, G).

% sat(+ListOfLists, +List, -List)
sat([[L|_]|F], [L|V]):-
    M is -L,
    filter(F, L, M, G),
    sat(G, V).
sat([[L|K]|F], [M|V]):-
    K \== [],
    M is -L,
    filter(F, M, L, G),
    sat([K|G], V).
sat([], []).

Вот пример для тестового примера Джо Леманна:

?- sat([[1,-2],[-1,-2],[1,3]], X).
X = [1,-2] ;
X = [-1,-2,3] ;
No

Код, вдохновленный https://gist.github.com/rla/4634264.
Я думаю, теперь это вариант DPLL.