Related
step_n(0, I, I).
step_n(N, In, Out) :-
N > 0, plus(N1, 1, N), phase_step(In, T),
step_n(N1, T, Out).
phase_step is a function that transforms data.
Will this step_n run in almost the same memory as phase_step? If not, how should I rewrite it to do so? Will this depend on phase_step having a single solution?
EDIT: After some debugging using prolog_current_frame, I found out that if phase_step is a simple function like Out is In + 1, then optimization happens but not in my use case.
Why is TCO dependent on phase_step predicate?
Will this depend on phase_step having a single solution?
Kind of, but a bit stronger still: It depends on phase_step being deterministic, which means, not leaving any "choice points". A choice point is a future path to be explored; not necessarily one that will produce a further solution, but still something Prolog needs to check.
For example, this is deterministic:
phase_step_det(X, X).
It has a single solution, and Prolog does not prompt us for more:
?- phase_step_det(42, Out).
Out = 42.
The following has a single solution, but it is not deterministic:
phase_step_extrafailure(X, X).
phase_step_extrafailure(_X, _Y) :-
false.
After seeing the solution, there is still something Prolog needs to check. Even if we can tell by looking at the code that that something (the second clause) will fail:
?- phase_step_extrafailure(42, Out).
Out = 42 ;
false.
The following has more than one solution, so it is not deterministic:
phase_step_twosolutions(X, X).
phase_step_twosolutions(X, Y) :-
plus(X, 1, Y).
?- phase_step_twosolutions(42, Out).
Out = 42 ;
Out = 43.
Why is TCO dependent on phase_step predicate?
If there are further paths to be explored, then there must be data about those paths stored somewhere. That "somewhere" is some sort of stack data structure, and for every future path there must be a frame on the stack. This is why your memory usage grows. And with it, the computation time (the following uses copies of your step_n with my corresponding phase_step variants from above):
?- time(step_n_det(100_000, 42, Out)).
% 400,002 inferences, 0.017 CPU in 0.017 seconds (100% CPU, 24008702 Lips)
Out = 42 ;
% 7 inferences, 0.000 CPU in 0.000 seconds (87% CPU, 260059 Lips)
false.
?- time(step_n_extrafailure(100_000, 42, Out)).
% 400,000 inferences, 4.288 CPU in 4.288 seconds (100% CPU, 93282 Lips)
Out = 42 ;
% 100,005 inferences, 0.007 CPU in 0.007 seconds (100% CPU, 13932371 Lips)
false.
?- time(step_n_twosolutions(100_000, 42, Out)).
% 400,000 inferences, 4.231 CPU in 4.231 seconds (100% CPU, 94546 Lips)
Out = 42 ;
% 4 inferences, 0.007 CPU in 0.007 seconds (100% CPU, 548 Lips)
Out = 43 ;
% 8 inferences, 0.005 CPU in 0.005 seconds (100% CPU, 1612 Lips)
Out = 43 ;
% 4 inferences, 0.008 CPU in 0.008 seconds (100% CPU, 489 Lips)
Out = 44 ;
% 12 inferences, 0.003 CPU in 0.003 seconds (100% CPU, 4396 Lips)
Out = 43 ;
% 4 inferences, 0.009 CPU in 0.009 seconds (100% CPU, 451 Lips)
Out = 44 . % many further solutions
One way to explore this is using the SWI-Prolog debugger, which has a way of showing you alternatives (= choice points = future paths to be explored):
?- trace, step_n_det(5, 42, Out).
Call: (9) step_n_det(5, 42, _1496) ? skip % I typed 's' here.
Exit: (9) step_n_det(5, 42, 42) ? alternatives % I typed 'A' here.
[14] step_n_det(0, 42, 42)
Exit: (9) step_n_det(5, 42, 42) ? no debug % I typed 'n' here.
Out = 42 ;
false.
?- trace, step_n_extrafailure(5, 42, Out).
Call: (9) step_n_extrafailure(5, 42, _1500) ? skip
Exit: (9) step_n_extrafailure(5, 42, 42) ? alternatives
[14] step_n_extrafailure(0, 42, 42)
[14] phase_step_extrafailure(42, 42)
[13] phase_step_extrafailure(42, 42)
[12] phase_step_extrafailure(42, 42)
[11] phase_step_extrafailure(42, 42)
[10] phase_step_extrafailure(42, 42)
Exit: (9) step_n_extrafailure(5, 42, 42) ? no debug
Out = 42 ;
false.
All of those alternatives correspond to extra interpreter frames. If you use SWI-Prolog's visual debugger, it will also show you a graph representation of your stack, including all open choice points (though I've always found that hard to make sense of).
So if you want TCO and not grow the stack, you need your phase step to execute deterministically. You can do that by making the phase_step predicate itself deterministic. You can also put a cut after the phase_step call inside step_n.
Here are the calls from above with a cut after each phase_step:
?- time(step_n_det(100_000, 42, Out)).
% 400,001 inferences, 0.017 CPU in 0.017 seconds (100% CPU, 24204529 Lips)
Out = 42 ;
% 7 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 737075 Lips)
false.
?- time(step_n_extrafailure(100_000, 42, Out)).
% 400,000 inferences, 0.023 CPU in 0.023 seconds (100% CPU, 17573422 Lips)
Out = 42 ;
% 5 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 220760 Lips)
false.
?- time(step_n_twosolutions(100_000, 42, Out)).
% 400,000 inferences, 0.023 CPU in 0.023 seconds (100% CPU, 17732727 Lips)
Out = 42 ;
% 5 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 219742 Lips)
false.
Do not place cuts blindly, only once you understand where and why you really need them. Note how in the extrafailure case the cut only removes failures, but in the twosolutions case it removes actual solutions.
One helpful tool to understood code performance issues, notably unwanted non-determinism, is a ports profiler tool as found on e.g. ECLiPSe and Logtalk. The Logtalk ports_profiler tool is portable so we can use it here. We start by wrapping your code (from your gist link):
:- use_module(library(lists), []).
:- object(step).
:- public(step_n/3).
:- use_module(lists, [reverse/2]).
% pattern for the nth digit mth-coeffcient
digit_m(N, M, D) :-
divmod(M, N, Q, _), divmod(Q, 4, _, C),
(C = 0, D = 0; C = 1, D = 1; C = 2, D = 0; C = 3, D = -1).
calculate_digit_n(N, In, D) :-
calculate_digit_n_(N, In, D, 1, 0).
calculate_digit_n_(_, [], D, _, Acc) :- D1 is abs(Acc), divmod(D1, 10, _, D).
calculate_digit_n_(N, [I | Is], D, M, Acc) :-
digit_m(N, M, C), P is C*I, M1 is M+1, Acc1 is Acc+P,
calculate_digit_n_(N, Is, D, M1, Acc1).
phase_step(In, Out) :-
length(In, L), L1 is L + 1, phase_step_(In, Out, L1, 1, []).
phase_step_(_, Out, L, L, Acc) :- reverse(Out, Acc).
phase_step_(In, Out, L, N, Acc) :-
N < L, calculate_digit_n(N, In, D), N1 is N + 1,
phase_step_(In, Out, L, N1, [D | Acc]).
step_n(0, I, I).
step_n(N, In, Out) :-
prolog_current_frame(Fr), format('~w ', Fr),
N > 0, N1 is N - 1, phase_step(In, T),
step_n(N1, T, Out).
:- end_object.
%:- step_n(10, [1, 2, 3, 4, 5, 6, 7, 8], X).
And then (using SWI-Prolog as the backend as that is the Prolog system you told us you're using):
$ swilgt
...
?- {ports_profiler(loader)}.
% [ /Users/pmoura/logtalk/tools/ports_profiler/ports_profiler.lgt loaded ]
% [ /Users/pmoura/logtalk/tools/ports_profiler/loader.lgt loaded ]
% (0 warnings)
true.
?- logtalk_load(step, [debug(on), source_data(on)]).
% [ /Users/pmoura/step.pl loaded ]
% (0 warnings)
true.
?- step::step_n(10, [1, 2, 3, 4, 5, 6, 7, 8], X).
340 15578 30816 46054 61292 76530 91768 107006 122244 137482
X = [3, 6, 4, 4, 0, 6, 7, 8] .
?- ports_profiler::data.
------------------------------------------------------------------------------
Entity Predicate Fact Rule Call Exit *Exit Fail Redo Error
------------------------------------------------------------------------------
step calculate_digit_n/3 0 80 80 0 80 0 0 0
step calculate_digit_n_/5 0 720 720 0 720 0 0 0
step digit_m/3 0 640 640 40 600 0 0 0
step phase_step/2 0 10 10 0 10 0 0 0
step phase_step_/5 0 90 90 0 90 0 0 0
step step_n/3 1 10 11 0 11 0 0 0
------------------------------------------------------------------------------
true.
The *Exit column is for non-deterministic exists from the procedure box. For help with the tool and with interpreting the table results, see https://logtalk.org/manuals/devtools/ports_profiler.html But is clear by just a glance to the table that both phase_step/2 and step_n/3 are non-deterministic.
Update
Note that tail call optimization (TCO) doesn't mean or require the predicate to be deterministic. In your case, TCO can be applied by a Prolog compiler as the last call in the rule for the step_n/3 predicate is call to itself. That means that a stack frame can be saved on that specific recursive call. It doesn't mean that there are no choice-points being created by what precedes the recursive call. Using once/1 (as you mention on the comments) simply discards the choice-point created when phase_step/2 is called as that predicate itself is non-deterministic. That's what the table shows. The step_n/3 predicate is also non-deterministic and thus calling it creates a choice-point when the first argument is 0, which happens when you call the predicate with a zero on the first argument or when the proof for the query reaches the base case on this recursive definition.
modThreeModFive(X) :- X > 0, X < 1000, 0 is mod(X, 5) ; 0 is mod(X, 3).
I'm wondering what the simplest way would be to get the sum of values of X for which the statement is true, given that the number of solutions is finite.
Is this even possible in Prolog, or am I forced to fill a list with integers [1, 1000], and then test them individually?
Being so different from imperative languages, the easiest way is to use a library predicate:
?- [user].
|: modThreeModFive(X) :- X > 0, X < 1000, 0 is mod(X, 5) ; 0 is mod(X, 3).
|:
% user://1 compiled 0.05 sec, 1 clauses
true.
?- aggregate(sum(X),(between(1,1000,X),modThreeModFive(X)),Sum).
Sum = 266333.
Not every Prolog has such library, then the alternative would be to - as you already suggested - to first get the list of values, then sum them together:
?- findall(X,(between(1,1000,X),modThreeModFive(X)),L),sumlist(L,Sum).
L = [3, 5, 6, 9, 10, 12, 15, 15, 18|...],
Sum = 266333.
If your Prolog allows for non backtrackable assignment, you could implement by yourself some of the basic functionalities found in library(aggregate).
OT: the version summing a list shows a bug in your logic: 15 is a solution in both branches. Instead of disjunction, you should use an if/then/else construct. Like in
?- [user].
|: modThreeModFive(X) :- 0 is mod(X, 5) -> true ; 0 is mod(X, 3).
Warning: user://2:21:
Warning: Redefined static procedure modThreeModFive/1
Warning: Previously defined at user://1:8
|:
% user://2 compiled 0.00 sec, 1 clauses
true.
?- findall(X,(between(1,1000,X),modThreeModFive(X)),L),sumlist(L,Sum).
L = [3, 5, 6, 9, 10, 12, 15, 18, 20|...],
Sum = 234168.
or just a cut !/0 to commit the first outcome. It's an useful exercise to find out the right position in such simple predicate...
I'm trying to generate a 4x4 magic square puzzle, and once a solution that is valid is found the list gets printed. I have the rules to print a given solution and generate a random board as well as solve it, but I don't know how I could have the generate fact be called until it returns true, and then print it. Here is my code:
check([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]) :-
A+B+C+D=:=34,
E+F+G+H=:=34,
I+J+K+L=:=34,
M+N+O+P=:=34,
A+E+I+M=:=34,
B+F+J+N=:=34,
C+G+K+O=:=34,
D+H+L+P=:=34,
A+F+K+P=:=34,
D+G+J+M=:=34.
solve([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]) :-
permutation(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]),
check([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]).
generate :-
random_permutation(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]),
solve([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]),
printlist([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]]).
printlist([X|List]) :-
write(X),nl,
printlist(List).
I know there isn't looping in the traditional sense with Prolog, but I don't quite understand how I could run until a valid case is found(also I am aware the brute force method used here could take quite some time).
Any insight as to solving this would be greatly appreciated!
Your program is calling for another approach!
Just look at the goal:
permutation(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]),
This goals will produce 16! solutions. In other words: 20 922 789 888 000. So, in case you have a fast computer at your disposal, ...
To improve this situation, we have to reduce the number of solutions or answers. But, how? Prolog has something very nice: the logic variable. That is, we can subsume many solutions within a single answer using constraints. In this case library(clpfd) will help:
?- Xs = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], Xs ins 1..16, all_different(Xs).
Xs = [A, B, C, D, E, F, G, H, I|...],
A in 1..16,
all_different([A, B, C, D, E, F, G, H|...]),
B in 1..16,
C in 1..16,
D in 1..16,
E in 1..16,
F in 1..16,
G in 1..16,
H in 1..16,
I in 1..16,
J in 1..16,
K in 1..16,
L in 1..16,
M in 1..16,
N in 1..16,
O in 1..16,
P in 1..16.
One answer now subsumes all twentytrillion solutions! With further constraints we can now write:
:- use_module(library(clpfd)).
magquad_([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]], Zs) :-
Xs = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P],
Xs ins 1..16,
all_different(Xs),
Zs = [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P],
A+B+C+D#=34,
E+F+G+H#=34,
I+J+K+L#=34,
M+N+O+P#=34,
A+E+I+M#=34,
B+F+J+N#=34,
C+G+K+O#=34,
D+H+L+P#=34,
A+F+K+P#=34,
D+G+J+M#=34.
magquad(Xss) :-
magquad_(Xss, Zs), % use of core-relation or model
labeling([], Zs). % separate labeling
Let the printing be done by the toplevel!
?- time(magquad(Xss)).
% 106,412 inferences, 0.052 CPU in 0.053 seconds (99% CPU, 2049006 Lips)
Xss = [[1, 2, 15, 16], [12, 14, 3, 5], [13, 7, 10, 4], [8, 11, 6, 9]] ;
% 36,910 inferences, 0.027 CPU in 0.027 seconds (99% CPU, 1384976 Lips)
Xss = [[1, 2, 15, 16], [13, 14, 3, 4], [12, 7, 10, 5], [8, 11, 6, 9]] ;
% 209,488 inferences, 0.089 CPU in 0.089 seconds (100% CPU, 2348606 Lips)
Xss = [[1, 2, 16, 15], [13, 14, 4, 3], [12, 7, 9, 6], [8, 11, 5, 10]] ...
As you can see, Prolog can now ridiculously fast find all those magic squares!
I'm trying to write langford sequence.
like this:
73 ?- langford4(L).
L = [4, 1, 3, 1, 2, 4, 3, 2] ;
L = [2, 3, 4, 2, 1, 3, 1, 4] ;
This is what i have done:
prefix([H|T],L):-cat([H|T],_,L).
sublist(S,L):-prefix(P,L), posfix(S,P).
posfix([H|T],L):-cat(_,[H|T],L).
langford42(L):-
L = [_,_,_,_,_,_,_,_],
sublist([1,_,1], L),
sublist([2,_,_,2], L),
sublist([3,_,_,_,3], L),
sublist([4,_,_,_,_,4], L).
or this:
langford(L):-
[X,_,_,_,_,X,_,_],
[_,Y,_,Y,_,_,_,_],
[_,_,Z,_,_,_,Z,_],
[_,_,_,_,P,_,_,P].
thanks.
don't get your question, your code seems fine, but anyway the problem, when generalized, is nice: I tried solving with CLP(FD) and the simpler library builtins
% two copies of each number k are k units apart
% constraint solution: would be nice to know how we could speedup this one...
langford_c(N, S) :-
M is N*2,
length(S, M),
S ins 1..N,
distances(S, S),
findall(I-2, between(1,N,I), Cs),
global_cardinality(S, Cs),
label(S).
distances([N|T], S) :-
element(I, S, N),
element(J, S, N),
J #= I + N + 1,
distances(T, S).
distances([], _).
% simple nth1/3 based solution
langford_n(N, S) :-
M is N*2,
length(S, M),
distances(S, 1, N).
distances(S, P, C) :-
P =< C, !,
nth1(I, S, P),
nth1(J, S, P),
J is I + P + 1,
Q is P + 1,
distances(S, Q, C).
distances(_, _, _).
with these result
?- time(langford_n(4, S)).
% 1,102 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 2598909 Lips)
S = [4, 1, 3, 1, 2, 4, 3, 2] ;
% 1,404 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 2679103 Lips)
S = [2, 3, 4, 2, 1, 3, 1, 4] ;
% 1,234 inferences, 0.001 CPU in 0.001 seconds (54% CPU, 2064308 Lips)
false.
?- time(langford_c(4, S)).
% 1,302,863 inferences, 0.489 CPU in 0.491 seconds (100% CPU, 2664067 Lips)
S = [2, 3, 4, 2, 1, 3, 1, 4] ;
% 958,979 inferences, 0.367 CPU in 0.371 seconds (99% CPU, 2611630 Lips)
S = [4, 1, 3, 1, 2, 4, 3, 2] ;
% 359,396 inferences, 0.137 CPU in 0.141 seconds (98% CPU, 2614215 Lips)
false.
I want to have a list L with three elements A,B, and C with the following constraint,
:- use_module(library(clpfd)).
L[A,B,C], L ins 1..3, A#=B+C.
But, it gives an error - Syntax error: Operator expected.
Just answering, so that the question goes off the list of unanswered questions:
The error could be also the (:-)/2, not only the missing (=)/2. So the following
session wurks:
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.1.11)
Copyright (c) 1990-2014 University of Amsterdam, VU Amsterdam
?- use_module(library(clpfd)).
% library(occurs) compiled into occurs 0.00 sec, 14 clauses
% library(apply_macros) compiled into apply_macros 0.01 sec, 51 clauses
% library(assoc) compiled into assoc 0.01 sec, 103 clauses
% library(clpfd) compiled into clpfd 0.12 sec, 2,694 clauses
true.
?- L=[A,B,C], L ins 1..3, A#=B+C.
L = [A, B, C],
A in 2..3,
B+C#=A,
B in 1..2,
C in 1..2.
In the above we only got as far as stating a problem including equations
and variable ranges. To enumerate solutions one has to use
the label/2 predicate as well:
?- L=[A,B,C], L ins 1..3, A#=B+C, label(L).
L = [2, 1, 1],
A = 2,
B = C, C = 1
L = [3, 1, 2],
A = 3,
B = 1,
C = 2
L = [3, 2, 1],
A = 3,
B = 2,
C = 1.
Bye