3

I am making a sudoku solver with prolog using clpfd library. I have to trace the backtracks and every squares labeled with row and column and the number it gets in the following form:

(1 ,1 ,1)              
(9 ,2 ,1)              
BT
(5 ,2 ,1)              

My question is how can I get the above information from the algorithm?

Another question: Does the algorithm observe arc-consistency rules by itself?

false
  • 10,264
  • 13
  • 101
  • 209
Kosar Kazemi
  • 65
  • 1
  • 6
  • 1
    Why do you need traces in the first place? And, arc-consistency is observed by clpfd, trivially. – false Jan 30 '17 at 12:20
  • All I need is to know which variable takes which value in every step of the algorithm. Tracing was the first idea came to my mind. – Kosar Kazemi Jan 30 '17 at 12:55
  • You can observe a lot and without tracing at the [tag:prolog-toplevel] by simply issuing queries with partially instantiated arguments. – false Jan 30 '17 at 13:00
  • Thanks. What about backtracking? How to know when does the algorithm backtrack? – Kosar Kazemi Jan 30 '17 at 13:05
  • That's very difficult - and pretty pointless: the very precise kind of consistency is much more important. – false Jan 30 '17 at 13:07
  • I did not find any proper code to find every time a value assigned to a variable. could you please tell me how to do this? I am using a code like this : http://stackoverflow.com/questions/5617126/mini-sudoku-solver-in-prolog-stops-partway-through?rq=1 – Kosar Kazemi Jan 30 '17 at 13:26

1 Answers1

2

I don't think this is a particularly good idea, but here is something using SWI-Prolog's attributed variables that prints the bindings of a set of variables whenever one of them becomes bound:

:- use_module(library(clpfd)).

% Vars is a list of Name-Variable pairs where Variable is a free variable
% and Name is an atom or some other identifier for the variable.
trace_vars(Vars) :-
    maplist(trace_var(Vars), Vars).

trace_var(Vars, Id-V) :-
    when(ground(V), print_new_binding(Vars, Id-V)).

print_new_binding(Vars, Id-V) :-
    format('new binding ~w, all bindings now: ~w~n', [Id-V, Vars]).

You can use this to "trace" things, in a sense:

?- Vars = [a-A,b-B,c-C], trace_vars(Vars), [A,B,C] ins 0..1, A #< B, B #< C.
new binding a-0, all bindings now: [a-0,b-_G211,c-_G217]
new binding b-1, all bindings now: [a-0,b-1,c-_G217]
false.

This only prints new bindings, including for variables that were bound before, but it does not print the moment when variables become unbound on backtracking. That information is implicit (and would need ugly hacks to become explicit):

?- Vars = [a-A,b-B,c-C], trace_vars(Vars), [A,B,C] ins 0..1, labeling([], [A,B,C]).
new binding a-0, all bindings now: [a-0,b-_G208,c-_G214]
new binding b-0, all bindings now: [a-0,b-0,c-_G214]
new binding c-0, all bindings now: [a-0,b-0,c-0]
Vars = [a-0, b-0, c-0],
A = B, B = C, C = 0 ;
new binding c-1, all bindings now: [a-0,b-0,c-1]
Vars = [a-0, b-0, c-1],
A = B, B = 0,
C = 1 ;
new binding b-1, all bindings now: [a-0,b-1,c-_G214]
new binding c-0, all bindings now: [a-0,b-1,c-0]
Vars = [a-0, b-1, c-0],
A = C, C = 0,
B = 1 ;
new binding c-1, all bindings now: [a-0,b-1,c-1]
Vars = [a-0, b-1, c-1],
A = 0,
B = C, C = 1 ;
new binding a-1, all bindings now: [a-1,b-_G208,c-_G214]
...

For your use case, use coordinates as identifiers in the Vars list, e.g., [(1,1)-Var11, (1,2)-Var12, ...].

I don't think watching clpfd at work in this way will enlighten you, though.

Edit: As mat points out in the comments, adding a failing second clause to print_new_binding/2 allows you to revisit a variable before its binding is undone:

print_new_binding(_Vars, Id-_V) :-
    format('undo binding for ~w~n', [Id]),
    false.
Isabelle Newbie
  • 9,258
  • 1
  • 20
  • 32
  • 1
    Instead of `when(ground(Var), Goal)`, consider using `freeze(Var, Goal)`. To show when a variable becomes *unbound* again, you only need a second clause for `Goal`, which prints "Variable is unbound" and then *fails*. On backtracking, you will see this emitted every time a binding is undone! – mat Jan 30 '17 at 16:58
  • Awesome tip, thanks! I added an edit. Concerning `freeze/2` vs. `when/2`, I find the latter clearer to read. Are there any relevant differences between `freeze(Var, Goal)` and `when(nonvar(Var), Goal)`? – Isabelle Newbie Jan 30 '17 at 18:25
  • 1
    As I see it, a problem of `when/2` is that it can easily lead users to believe that a general condition like `when(Cond, ...)` is admissible, when in reality, the admissible conditions are very limited. In sufficiently advanced Prolog systems, such mistakes are easily detected and lead to a *domain error*, but previously, such a mistake could lead to hours and days of wading through traces, not knowing why the condition fails when it "obviously" holds. This happened to a friend, by the way ;-) `freeze/2` is less general, but suffices in this use case, and I would prefer the simpler construct. – mat Jan 30 '17 at 18:44