Sudoku solver performance problems - performance

I have a problem with my prolog solver for sudoku. It's working, but the performance is reeaaaaly bad. With small ones it's working just fine, but bigger ones like 9x9 or more take 10 minutes or more sadly. I want to leave it for an indefinite size like it is right now. Can anyone help?
solve_sudoku(Rows,Sol):-
length(Rows,Max),
maplist(same_length(Rows),Rows),
append(Rows, List), List ins 1..Max,
maplist(all_distinct, Rows),
transpose(Rows, Columns),
maplist(all_distinct,Columns),
maplist(label,Rows),
boxes(Boxes,Rows),
maplist(all_distinct,Boxes),
boxes_distinct(Boxes),
Sol = Rows.
boxes(Bs,M) :-
length(M,Len),
Sq is round(sqrt(Len)),
findall(B, (between(1, Sq, R),
between(1, Sq, C),
block(M, Sq, R, C, B)), Bs).
cell(M, R,C, V) :-
nth1(R,M,Row), nth1(C,Row,V).
block(M, Sq, R,C, B) :-
findall(V, (between(1, Sq, X),
between(1, Sq, Y),
I is (R-1) * Sq + X,
J is (C-1) * Sq + Y,
cell(M, I, J, V)), B).
boxes_distinct([]).
boxes_distinct([BH|BT]):-
all_distinct(BH),
boxes_distinct(BT).
The input is a list of lists with the sudoku to solve and the output is the solved sudoku as a list.

I think you shoud call
maplist(label,Rows)
after
boxes_distinct(Boxes)
usually you should call label or labelling after declaring all constraint.
and use "labeling predicate with ff or ffc option" instead of "label"
may increse efficiency.

boxes_distinct(Boxes) seems useless, being already covered by maplist(all_distinct,Boxes), and use label(List) (instead of maplist(label,Rows)) near the end of clause. Anyway, the main problem has been indicated by #gabrielte (+1)

Related

Program decomposition and lazy_findall

I like the idea of lazy_findall as it helps me with keeping predicates separated and hence program decomposition.
What are the cons of using lazy_findall and are there alternatives?
Below is my "coroutine" version of the branch and bound problem.
It starts with the problem setup:
domain([[a1, a2, a3],
[b1, b2, b3, b4],
[c1, c2]]).
price(a1, 1900).
price(a2, 750).
price(a3, 900).
price(b1, 300).
price(b2, 500).
price(b3, 450).
price(b4, 600).
price(c1, 700).
price(c2, 850).
incompatible(a2, c1).
incompatible(b2, c2).
incompatible(b3, c2).
incompatible(a2, b4).
incompatible(a1, b3).
incompatible(a3, b3).
Derived predicates:
all_compatible(_, []).
all_compatible(X, [Y|_]) :- incompatible(X, Y), !, fail.
all_compatible(X, [_|T]) :- all_compatible(X, T).
list_price(A, Threshold, P) :- list_price(A, Threshold, 0, P).
list_price([], _, P, P).
list_price([H|T], Threshold, P0, P) :-
price(H, P1),
P2 is P0 + P1,
P2 =< Threshold,
list_price(T, Threshold, P2, P).
path([], []).
path([H|T], [I|Q]) :-
member(I, H),
path(T, Q),
all_compatible(I, Q).
The actual logic:
solution([], Paths, Paths, Value, Value).
solution([C|D], Paths0, Paths, Value0, Value) :-
( list_price(C, Value0, V)
-> ( V < Value0
-> solution(D, [C], Paths, V, Value)
; solution(D, [C|Paths0], Paths, Value0, Value)
)
; solution(D, Paths0, Paths, Value0, Value)
).
The glue
solution(Paths, Value) :-
domain(D),
lazy_findall(P, path(D, P), Paths0),
solution(Paths0, [], Paths, 5000, Value).
Here is an alternative no-lazy-findall solution by #gusbro: https://stackoverflow.com/a/68415760/1646086
I am not familiar with lazy_findall but I observe two "drawbacks" with the presented approach:
The code is not as decoupled as one might want, because there is still a mix of "declarative" and "procedural" code in the same predicate. I am putting quotes around the terms because they can mean a lot of things but here I see that path/2 is concerned with both generating paths AND ensuring that they are valid. Similarly solution/5 (or rather list_price/3-4) is concerned with both computing the cost of paths and eliminating too costly ones with respect to some operational bound.
The "bounding" test only happens on complete paths. This means that in practice all paths are generated and verified in order to find the shortest one. It does not matter for such a small problem but might be important for larger instances. Ideally, one might want to detect for instance that the partial path [a1,?,?] will never bring a solution less than 2900 without trying all values for b and c.
My suggestion is to instead use clpfd (or clpz, depending on your system) to solve both issues. With clpfd, one can first state the problem without concern for how to solve it, then call a predefined predicate (like labeling/2) to solve the problem in a (hopefully) clever way.
Here is an example of code that starts from the same "setup" predicates as in the question.
state(Xs,Total):-
domain(Ds),
init_vars(Ds,Xs,Total),
post_comp(Ds,Xs).
init_vars([],[],0).
init_vars([D|Ds],[X|Xs],Total):-
prices(D,P),
length(D,N),
X in 1..N,
element(X, P, C),
Total #= C + Total0,
init_vars(Ds,Xs,Total0).
prices([],[]).
prices([V|Vs],[P|Ps]):-
price(V,P),
prices(Vs,Ps).
post_comp([],[]).
post_comp([D|Ds],[X|Xs]):-
post_comp0(Ds,D,Xs,X),
post_comp(Ds,Xs).
post_comp0([],_,[],_).
post_comp0([D2|Ds],D1,[X2|Xs],X1):-
post_comp1(D1,1,D2,X1,X2),
post_comp0(Ds,D1,Xs,X1).
post_comp1([],_,_,_,_).
post_comp1([V1|Vs1],N,Vs2,X1,X2):-
post_comp2(Vs2,1,V1,N,X2,X1),
N1 is N+1,
post_comp1(Vs1,N1,Vs2,X1,X2).
post_comp2([],_,_,_,_,_).
post_comp2([V2|Vs2],N2,V1,N1,X2,X1):-
post_comp3(V2,N2,X2,V1,N1,X1),
N3 is N2 + 1,
post_comp2(Vs2,N3,V1,N1,X2,X1).
post_comp3(V2,N2,X2,V1,N1,X1) :-
( ( incompatible(V2,V1)
; incompatible(V1,V2)
)
-> X2 #\= N2 #\/ X1 #\= N1
; true
).
Note that the code is relatively straightforward, except for the (quadruple) loop to post the incompatibility constraints. This is due to the way I wanted to reuse the predicates in the question. In practice, one might want to change the way the data is presented.
The problem can then be solved with the following query (in SWI-prolog):
?- state(Xs, T), labeling([min(T)], Xs).
T = 1900, Xs = [2, 1, 2] ?
In SICStus prolog, one can write instead:
?- state(Xs, T), minimize(labeling([], Xs), T).
Xs = [2,1,2], T = 1900 ?
Another short predicate could then transform back the [2,1,2] list into [a2,b1,c2] if that format was expected.

Fastest way between two cities

I need to find the fastest way to travel from one city to another. I have something like
way(madrid, barcelona, 4).
way(barcelona, paris, 5).
way(madrid, londres, 3).
way(londres,paris,1).
I have come up with a predicate shortway(A,B,C,D) where C is the list of towns between A and B and D the distance.
so I have
shortway(A,B,C,D):-
way(A,B,_,_) , (A,_,C,D). D<C.
shortway(A,_,C).
I trying my best but I really cant get it to work!
You have a bunch of problems with your code! First of all, way/3 has arity 3, not 4, so calling way(A,B,_,_,) is clearly not going to do what you think. Second, I have no idea what you're trying to do with (A,_,C,D). The period after this signifies the end of the predicate! So the next line, D<C. is just a free-floating query that cannot be fulfilled. And then shortway(A,_,C) is basically a fact, with three singletons, but it would define a shortway/3 clause when the previous one is a shortway/4 clause.
There really isn't enough that's on the right track here to try and recover. It looks here like you are extremely confused about even the basics of Prolog. I would strongly encourage you to go back to the beginning and start over. You can't rush Prolog! And this code looks like you're trying to make a combustion engine by banging rocks together.
I wrote some line of code to help you, but as https://stackoverflow.com/users/812818/daniel-lyons said, it's better than you learn something easier before.
To solve your problem I advice you to read, at least, the first 3 chapters of this book: http://www.learnprolognow.org/lpnpage.php?pageid=online and do the practical session at paragraph 3.4.
Then, you could take a look at my code (you can find some explenation of it here:Out of local stack error in Prolog route planner .
Here the code
way(madrid, barcelona, 4).
way(barcelona, paris, 5).
way(madrid, londres, 3).
way(londres,paris,1).
shortway(From, To):- findall(Journey, travel(From, To, Journey, _) , Travels_list),
findall(Total_distance, travel(From, To, _, Total_distance) , Distances_list),
min_member(Y, Distances_list), find_minimum_index(Y, Distance_list, 1, Distance_index),
find_journey(Distance_index, Travels_list, 0, Shortest_path),
format('The shortest path is ~w', [Shortest_path]).
travel(From, To, Journey, Total_distance) :- dif(From, To),
AccDistance is 0,
path(From, To, [From], Journey, AccDistance, Total_distance).
path(From, To, Passed_cities, go(From, To), AccDistance, Total_distance) :- way(From, To, Way_distance),
Total_distance is AccDistance + Way_distance.
path(From, To, Passed_cities, go(From, Intermediate, GO), AccDistance, Total_distance) :- way(From, Intermediate, Way_distance),
dif(Intermediate, To),
\+ member(Intermediate, Passed_cities),
NewAccDistance is AccDistance + Way_distance,
path(Intermediate, To, [Intermediate|Passed_cities], GO, NewAccDistance, Total_distance).
min_member(Min, [H|T]) :- min_member_(T, H, Min).
min_member_([], Min, Min).
min_member_([H|T], Min0, Min) :-
( H >= Min0
-> min_member_(T, Min0, Min)
; min_member_(T, H, Min)
).
find_minimum_index(X, [], N, I) :- fail.
find_minimum_index(X, [X|T], N, I) :- I is N, !.
find_minimum_index(X, [H|T], N, I) :- H \= X, increment(N, N1), find_minimum_index(X, T, N1, I).
find_journey(I, [H|T], N, Elemento) :- N = I, Elemento = H, !.
find_journey(I, [H|T], N, Elemento) :- N \= I, increment(N, N1), find_journey(I, T, N1, Elemento).
increment(X, X1) :- X1 is X+1.
Then you call, for example
?:- shortway(madrid,paris).
and it will return
"The shortest path is go(madrid, londres, go(londres,paris))"
which distance is, 4
rather than
go(madrid, barcelona, go(barcelona, madrid)
which distance is 9.
Summing up: calling shortway/2, with the predicates findall/3 you'll find the lists of all possible pathes and their relative distances, respectively, then you'll browse the list of the distances to find the index of the minimum element and so using it to find the shortest path from the list of all pathes found previously.

Prolog - jigoku solver - run time

I'm a total newbie to Prolog (as in: I've only done the Prolog chapter in 7 languages in 7 weeks), so general comments to any of the code below are very welcome.
First of all: What is a jigoku? It's like a sudoku, except that you get an empty grid, and within each 3x3 block, inequalities between adjacent slots are given. Example here: http://krazydad.com/jigoku/books/KD_Jigoku_CH_8_v18.pdf. You still need to fill up the grid such that each row, column and block contains the numbers 1-9.
I've tried to implement a solver based on this sudoku solver: http://programmablelife.blogspot.co.uk/2012/07/prolog-sudoku-solver-explained.html. For debugging reasons, I started with a 4x4 example that works really well:
:- use_module(library(clpfd)).
small_jidoku(Rows, RowIneqs, ColIneqs) :-
Rows = [A,B,C,D],
append(Rows, Vs), Vs ins 1..4,
maplist(all_distinct, Rows),
transpose(Rows, Columns),
maplist(all_distinct, Columns),
blocks(A, B), blocks(C,D),
maplist(label, Rows),
fake_check_ineqs(Rows, RowIneqs),
fake_check_ineqs(Columns, ColIneqs),
pretty_print([A,B,C,D]).
blocks([], []).
blocks([A,B|Bs1], [D,E|Bs2]) :-
all_distinct([A,B,D,E]),
blocks(Bs1, Bs2).
fake_check_ineqs([],[]).
fake_check_ineqs([Head|Tail], [Ineq1|TailIneqs]) :-
Head = [A,B,C,D],
atom_chars(Ineq1, [X1,X2]),
call(X1, A, B),
call(X2, C, D),
fake_check_ineqs(Tail, TailIneqs).
pretty_print([]).
pretty_print([Head | Tail]) :-
print(Head),
print('\n'),
pretty_print(Tail).
I then solve the following example:
time(small_jidoku([[A1,A2,A3,A4],[B1,B2,B3,B4],[C1,C2,C3,C4],[D1,D2,D3,D4]],[><,<>,<<,<<],[><,<<,<>,>>])).
This runs in about 0.5 seconds tops. However, I've also tried to solve it with
time(small_jidoku([A,B,C,D],[><,<>,<<,<<],[><,<<,<>,>>])).
and this seems to take ages.
Can anyone explain why it takes the solver much longer when I don't specify that each row has 4 elements? My naive answer to this is that Prolog, if not told the actual format of my rows, will also try to explore smaller/bigger rows, hence wasting time on e.g. rows of length 5, but is this actually true?
My second question is about the 9x9 version, that is very much like the 4x4 except that the blocks are of course bigger and that there is more testing to be done when checking inequalities. The code is below:
:- use_module(library(clpfd)).
jidoku(Rows, RowIneqs, ColIneqs) :-
Rows = [A,B,C,D,E,F,G,H,I],
append(Rows, Vs), Vs ins 1..9,
maplist(all_distinct, Rows),
transpose(Rows, Columns),
maplist(all_distinct, Columns),
blocks(A, B, C), blocks(D, E, F), blocks(G, H, I),
maplist(label, Rows),
check_ineqs(Rows, RowIneqs),
check_ineqs(Columns, ColIneqs),
pretty_print([A,B,C,D,E,F,G,H,I]).
blocks([], [], []).
blocks([A,B,C|Bs1], [D,E,F|Bs2], [G,H,I|Bs3]) :-
all_distinct([A,B,C,D,E,F,G,H,I]),
blocks(Bs1, Bs2, Bs3).
check_ineqs([],[]).
check_ineqs([Head|Tail], [Ineq1|TailIneqs]) :-
Head = [A,B,C,D,E,F,G,H,I],
atom_chars(Ineq1, [X1, X2, X3, X4, X5, X6]),
call(X1, A, B),
call(X2, B, C),
call(X3, D, E),
call(X4, E, F),
call(X5, G, H),
call(X6, H, I),
check_ineqs(Tail, TailIneqs).
The test example:
time(jidoku([[A1,A2,A3,A4,A5,A6,A7,A8,A9],
[B1,B2,B3,B4,B5,B6,B7,B8,B9],
[C1,C2,C3,C4,C5,C6,C7,C8,C9],
[D1,D2,D3,D4,D5,D6,D7,D8,D9],
[E1,E2,E3,E4,E5,E6,E7,E8,E9],
[F1,F2,F3,F4,F5,F6,F7,F8,F9],
[G1,G2,G3,G4,G5,G6,G7,G8,G9],
[H1,H2,H3,H4,H5,H6,H7,H8,H9],
[I1,I2,I3,I4,I5,I6,I7,I8,I9]],
[<>>><>,<<<>><,<<<><>,<><<><,>>><><,><>><>,<>>><>,<>><><,><<>>>],
[<<<><>,><<>>>,<><<><,><<<>>,><><<<,<><><>,<>>>><,><><><,<>><>>])).
and this one has been running overnight without reaching any conclusion and at this point, I have no clue whatsoever what is going wrong. I expected some scaling issues, but not of this proportion!
It would be great if someone who actually knows what they're doing could shine a light on this! Thanks already!
Here is the version of your code I had in mind (other predicates kept unchanged):
ineqs(Cells, Ineq) :-
atom_chars(Ineq, Cs),
maplist(primitive_declarative, Cs, Ds),
ineqs_(Ds, Cells).
ineqs_([], _).
ineqs_([Op1,Op2|Ops], [A,B,C|Cells]) :-
call(Op1, A, B),
call(Op2, B, C),
ineqs_(Ops, Cells).
primitive_declarative(<, #<).
primitive_declarative(>, #>).
Notice that it does not do the generality of the code justice to call the predicate "check_...", because the predicate states what holds and can be used in several directions: Yes, it can be used to check if the constraints hold, but it can also be used to state that the constraints must hold for some variables. I therefore avoid imperatives and use more declarative names.
You use ineqs/2 in jidoku/3 with: maplist(ineqs, Rows, RowsIneqs) etc.
Your example and the result with the new version, using SWI 7.3.2:
?- length(Rows, 9), maplist(same_length(Rows), Rows),
time(jidoku(Rows,
[<>>><>,<<<>><,<<<><>,<><<><,>>><><,><>><>,<>>><>,<>><><,><<>>>],
[<<<><>,><<>>>,<><<><,><<<>>,><><<<,<><><>,<>>>><,><><><,<>><>>])),
maplist(writeln, Rows).
% 2,745,471 inferences, 0.426 CPU in 0.432 seconds (99% CPU, 6442046 Lips)
[1,5,4,8,7,2,6,9,3]
[2,3,9,1,6,5,7,4,8]
[6,7,8,3,9,4,2,5,1]
[3,4,1,2,5,6,8,7,9]
[9,6,5,7,1,8,3,2,4]
[8,2,7,9,4,3,1,6,5]
[4,9,3,6,2,1,5,8,7]
[7,8,2,5,3,9,4,1,6]
[5,1,6,4,8,7,9,3,2]
Rows = [[1, 5, 4, 8, 7, 2, 6, 9|...], ...].
In fact, note that no labeling at all is required to compute the unique solution in this particular case, because the constraint solver is strong enough to reduce all domains to singleton sets.
In your previous version, all the time was needlessly wasted naively generating permutations that were eventually seen to be inconsistent. With the new version, the constraint solver has a chance to apply this knowledge earlier.
It is therefore recommended to first state all constraints, and only then to invoke labeling/2 to search for concrete solutions, as explained in the CLP(FD) manual.

CLP in Prolog involving consecutive sums in a list

Example of my CLP problem (this is a small part of a larger problem which uses the clpfd library):
For a list of length 5, a fact el_sum(Pos,N,Sum) specifies that the N consecutive elements starting from position Pos (index from 1) have sum equal to Sum. So if we have
el_sum(1,3,4).
el_sum(2,2,3).
el_sum(4,2,5).
Then [1,2,1,4,1] would work for this example since 1+2+1=4, 2+1=3, 4+1=5.
I'm struggling with how to even start using the el_sum's to find solutions with an input list [X1,X2,X3,X4,X5]. I'm thinking I should use findall but I'm not really getting anywhere.
(My actual problem is much bigger than this so I'm looking for a solution that doesn't just work for three facts and a small list).
Thanks!
You are mixing here the monotonic world of constraints with some non-monotonic quantification. Don't try to mix them too closely. Instead, first transform those facts into, say, a list of terms.
el_sums(Gs) :-
G = el_sum(_,_,_),
findall(G, G, Gs).
And then, only then, start with the constraint part that will now remain monotonic. So:
?- el_sums(Gs), length(L5,5), maplist(l5_(L5), Gs).
l5_(L5, el_sum(P, N, S)) :-
length([_|Pre], P),
length(Cs, N),
phrase((seq(Pre),seq(Cs),seq(_)), L5),
list_sum(Cs,S).
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
Not sure this will help, I don't understand your workflow... from where the list do come ? Anyway
:- [library(clpfd)].
el_sum(Pos,N,Sum) :-
length(L, 5),
L ins 0..100,
el_sum(Pos,N,Sum,L),
label(L), writeln(L).
el_sum(P,N,Sum,L) :-
N #> 0,
M #= N-1,
Q #= P+1,
el_sum(Q,M,Sum1,L),
element(N,L,T),
Sum #= Sum1 + T.
el_sum(_P,0,0,_L).
yields
?- el_sum(1,2,3).
[0,3,0,0,0]
true ;
[0,3,0,0,1]
true ;
...

Prolog Programming

I have made two programs in Prolog for the nqueens puzzle using hill climbing and beam search algorithms.
Unfortunately I do not have the experience to check whether the programs are correct and I am in dead end.
I would appreciate if someone could help me out on that.
Unfortunately the program in hill climbing is incorrect. :(
The program in beam search is:
queens(N, Qs) :-
range(1, N, Ns),
queens(Ns, [], Qs).
range(N, N, [N]) :- !.
range(M, N, [M|Ns]) :-
M < N,
M1 is M+1,
range(M1, N, Ns).
queens([], Qs, Qs).
queens(UnplacedQs, SafeQs, Qs) :-
select(UnplacedQs, UnplacedQs1,Q),
not_attack(SafeQs, Q),
queens(UnplacedQs1, [Q|SafeQs], Qs).
not_attack(Xs, X) :-
not_attack(Xs, X, 1).
not_attack([], _, _) :- !.
not_attack([Y|Ys], X, N) :-
X =\= Y+N,
X =\= Y-N,
N1 is N+1,
not_attack(Ys, X, N1).
select([X|Xs], Xs, X).
select([Y|Ys], [Y|Zs], X) :- select(Ys, Zs, X).
I would like to mention this problem is a typical constraint satisfaction problem and can be efficiency solved using the CSP module of SWI-Prolog. Here is the full algorithm:
:- use_module(library(clpfd)).
queens(N, L) :-
N #> 0,
length(L, N),
L ins 1..N,
all_different(L),
applyConstraintOnDescDiag(L),
applyConstraintOnAscDiag(L),
label(L).
applyConstraintOnDescDiag([]) :- !.
applyConstraintOnDescDiag([H|T]) :-
insertConstraintOnDescDiag(H, T, 1),
applyConstraintOnDescDiag(T).
insertConstraintOnDescDiag(_, [], _) :- !.
insertConstraintOnDescDiag(X, [H|T], N) :-
H #\= X + N,
M is N + 1,
insertConstraintOnDescDiag(X, T, M).
applyConstraintOnAscDiag([]) :- !.
applyConstraintOnAscDiag([H|T]) :-
insertConstraintOnAscDiag(H, T, 1),
applyConstraintOnAscDiag(T).
insertConstraintOnAscDiag(_, [], _) :- !.
insertConstraintOnAscDiag(X, [H|T], N) :-
H #\= X - N,
M is N + 1,
insertConstraintOnAscDiag(X, T, M).
N is the number of queens or the size of the board (), and , where , being the position of the queen on the line .
Let's details each part of the algorithm above to understand what happens.
:- use_module(library(clpfd)).
It indicates to SWI-Prolog to load the module containing the predicates for constraint satisfaction problems.
queens(N, L) :-
N #> 0,
length(L, N),
L ins 1..N,
all_different(L),
applyConstraintOnDescDiag(L),
applyConstraintOnAscDiag(L),
label(L).
The queens predicate is the entry point of the algorithm and checks if the terms are properly formatted (number range, length of the list). It checks if the queens are on different lines as well.
applyConstraintOnDescDiag([]) :- !.
applyConstraintOnDescDiag([H|T]) :-
insertConstraintOnDescDiag(H, T, 1),
applyConstraintOnDescDiag(T).
insertConstraintOnDescDiag(_, [], _) :- !.
insertConstraintOnDescDiag(X, [H|T], N) :-
H #\= X + N,
M is N + 1,
insertConstraintOnDescDiag(X, T, M).
It checks if there is a queen on the descendant diagonal of the current queen that is iterated.
applyConstraintOnAscDiag([]) :- !.
applyConstraintOnAscDiag([H|T]) :-
insertConstraintOnAscDiag(H, T, 1),
applyConstraintOnAscDiag(T).
insertConstraintOnAscDiag(_, [], _) :- !.
insertConstraintOnAscDiag(X, [H|T], N) :-
H #\= X - N,
M is N + 1,
insertConstraintOnAscDiag(X, T, M).
Same as previous, but it checks if there is a queen on the ascendant diagonal.
Finally, the results can be found by calling the predicate queens/2, such as:
?- findall(X, queens(4, X), L).
L = [[2, 4, 1, 3], [3, 1, 4, 2]]
If I read your code correctly, the algorithm you're trying to implement is a simple depth-first search rather than beam search. That's ok, because it should be (I don't see how beam search will be effective for this problem and it can be hard to program).
I'm not going to debug this code for you, but I will give you a suggestion: build the chess board bottom-up with
queens(0, []).
queens(N, [Q|Qs]) :-
M is N-1,
queens(M, Qs),
between(1, N, Q),
safe(Q, Qs).
where safe(Q,Qs) is true iff none of Qs attack Q. safe/2 is then the conjunction of a simple memberchk/2 check (see SWI-Prolog manual) and your not_attack/2 predicate, which on first sight seems to be correct.
A quick check on Google has found a few candidates for you to compare with your code and find what to change.
My favoured solution for sheer clarity would be the second of the ones linked to above:
% This program finds a solution to the 8 queens problem. That is, the problem of placing 8
% queens on an 8x8 chessboard so that no two queens attack each other. The prototype
% board is passed in as a list with the rows instantiated from 1 to 8, and a corresponding
% variable for each column. The Prolog program instantiates those column variables as it
% finds the solution.
% Programmed by Ron Danielson, from an idea by Ivan Bratko.
% 2/17/00
queens([]). % when place queen in empty list, solution found
queens([ Row/Col | Rest]) :- % otherwise, for each row
queens(Rest), % place a queen in each higher numbered row
member(Col, [1,2,3,4,5,6,7,8]), % pick one of the possible column positions
safe( Row/Col, Rest). % and see if that is a safe position
% if not, fail back and try another column, until
% the columns are all tried, when fail back to
% previous row
safe(Anything, []). % the empty board is always safe
safe(Row/Col, [Row1/Col1 | Rest]) :- % see if attack the queen in next row down
Col =\= Col1, % same column?
Col1 - Col =\= Row1 - Row, % check diagonal
Col1 - Col =\= Row - Row1,
safe(Row/Col, Rest). % no attack on next row, try the rest of board
member(X, [X | Tail]). % member will pick successive column values
member(X, [Head | Tail]) :-
member(X, Tail).
board([1/C1, 2/C2, 3/C3, 4/C4, 5/C5, 6/C6, 7/C7, 8/C8]). % prototype board
The final link, however, solves it in three different ways so you can compare against three known solutions.

Resources