Related
I am stuck on this problem for a while now.
Suppose I have some facts like so:
function([1,2,3],['A']).
function([4,5,6],['B']).
And I want to write a predicate like so:
mapSomeList(List,Result)
which can transform, say, the list of [[1,2,3],[4,5,6],[1,2,3]] into ['A','B','A'].
How can I use recursion to implement mapSomeList?
What I have tried:
mapSomeList([],[]).
mapSomeList([X|Rest],Result):-
function(X,Letter),
append(Letter,[X],Result),
mapSomeList(X,Rest).
But it just would not return the Result.
Thanks!
Two simple ways to solve this: one with append/3 and one without.
Solution 1 with append/3:
mapSomeList([],L,L).
mapSomeList([H|T],LT,LO):-
function(H,F),
append(LT,F,LT1),
mapSomeList(T,LT1,LO).
mapSomeList(List,Result):-
mapSomeList(List,[],Result).
?- mapSomeList([[1,2,3],[4,5,6],[1,2,3]],L).
L = [['A'], ['B'], ['A']]
Solution 2 without append/3:
mapSomeList([],[]).
find_pair([H|T],[F|T1]):-
function(H,F),
mapSomeList(T,T1).
?- mapSomeList([[1,2,3],[4,5,6],[1,2,3]],L).
L = [['A'], ['B'], ['A']]
Differences? Try to run the two goals inside time/1 (i used SWISH)
?- time(find_pair([[1,2,3],[4,5,6],[1,2,3]],L)).
with append/3: 14 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 378757 Lips)
without append/3: 6 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 377169 Lips)
So, you can see, already from this simple example, that the solution without append is faster.
how can I do the following.
I needed to define the predicate shownumber (X,N), which is true when the symbol X corresponds to the natural number N. For example, shownumber(s(zero),1) is true. Okay, now I've got a predicate:
shownumber (zero, 0).
shownumber (s (N), X): - shownumber (N, Y), X is Y + 1.
Now I need to use the shownumber (X, Y) predicate to define:
1) times (X, Y, Z) which is true if X * Y = Z.
2) quotient (X, Y, Q) which is true if X / Y = Q (in natural number arithmetic)
3) remainder (X, Y, R) which is true if X divided by Y gives the remainder R.
The quotient (X, Y, Q) and the remainder (X, Y, R) must be defined for Y = zero.
How can I do that? Could you help me with this one?
This code may help:
peano_redux.pl
It took me a long time to finish it and it still has bad corners. I tried to stay "close the Peano Axioms" but some shortcuts had to be taken - this is programming, not general theorem proving.
I also used the old-school & messy Peano notation s(s(s(s(z))) instead the far cleaner and appropriate list-based notation: [s,s,s,s].
It would be extremely cool to be able to set up a constraint between two
variables PN and NN, so that if NN is bound to a natural number, PN is
automatically bound to the coressponding Peano Number, and vice-versa.
That can probably be done with some effort using attributed variables, but I haven't thought about that.
Computation is expectedly very slow. It would be interesting to try tabling on the padd/pmult predicates.
The bidirectional transformation between Peano Numbers and Naturals exists in two version: One using CLP(FD) and one using basic Prolog. Comment out the version you don't want.
This is overall an interesting (but time-consuming exercise). It gives a lot of practice in debugging and thinking about control flow, and forces you to watch out for undesired unification, non-termination, early termination and special cases of bound/fresh variable configurations.
It also highlights the absolute need to write unit tests as you program, to give you a scaffolding for progress,
Seemingly innocuous rearrangements of code may cause a previously working Prolog program to fail or loop indefinitely for hard-to-explain reasons. Having test cases to get back on track avoids meaninglessly burning time on "fixing things until they work" and possibly "running tests by hand".
Unit tests are also of great use for communicating your problem's specification Programming courses should really orbit around that concept first - this isn't the 80s anymore.
Run all test cases by issuing command rtall for great success:
?- rt_all.
Correct to: "rtall"? yes
% PL-Unit: pm ................... done
% All 19 tests passed
% PL-Unit: pnat ......... done
% All 9 tests passed
% PL-Unit: pequal .... done
% All 4 tests passed
% PL-Unit: padd ................................. done
% All 33 tests passed
% PL-Unit: pless ................. done
% All 17 tests passed
% PL-Unit: pmult .......................
% 1,649 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 8951351 Lips)
.
% 3,097 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 10109979 Lips)
.
% 5,813 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 11183341 Lips)
.
% 2,598 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 9577492 Lips)
.
% 768 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 8491724 Lips)
.
% 1,847 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 9731501 Lips)
.
% 8,453,914 inferences, 0.668 CPU in 0.674 seconds (99% CPU, 12651865 Lips)
.
% 4,273 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 10987655 Lips)
.
% 8,389 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 11702424 Lips)
.
% 12,506 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 11900038 Lips)
.
% 45,453 inferences, 0.004 CPU in 0.004 seconds (100% CPU, 11844692 Lips)
. done
% All 34 tests passed
% PL-Unit: pqr ................Found: 16*13+8=216
Found: 37*12+8=452
Found: 7*53+1=372
Found: 28*7+13=209
Found: 33*14+6=468
Found: 23*5+19=134
Found: 21*3+3=66
Found: 31*8+1=249
Found: 14*20+9=289
Found: 5*2+4=14
Found: 4*9+0=36
Found: 30*3+6=96
Found: 40*11+16=456
Found: 11*4+8=52
Found: 10*12+3=123
Found: 18*20+0=360
Found: 5*61+2=307
Found: 46*2+0=92
Found: 1*215+0=215
Found: 47*7+30=359
. done
% All 17 tests passed
true.
Some references vaguely consulted:
Natural number
Peano axioms
Proofs involving the addition of natural
numbers
I have tried a few versions of a simple predicate which pulls random values out of the extra-logical universe and puts them into a list. I assumed that the version with the accumulator would be be tail-call optimized, as nothing happens after recursive call so a path to optimization exists, but it is not (it uses the "global stack"). On the other hand, the "naive version" has apparently been optimized into a loop. This is SWI Prolog.
Why would the accumulator version be impervious to tail-call optimization?
Here are the predicate versions.
Slowest, runs out of local stack space (expectedly)
Here, we just allow a head with function symbols to make things explicit.
% Slowest, and uses 4 inferences per call (+ 1 at the end of recursion).
% Uses "local stack" indicated in the "Stack limit (1.0Gb) exceeded"
% error at "Stack depth: 10,321,204":
% "Stack sizes: local: 1.0Gb, global: 7Kb, trail: 1Kb"
oracle_rands_explicit(Out,Size) :-
Size>0, !,
NewSize is Size-1,
oracle_rands_explicit(R,NewSize),
X is random_float,
Out = [X-Size|R].
oracle_rands_explicit([],0).
?- oracle_rands_explicit(X,4).
X = [0.7717053554954681-4, 0.9110187097066331-3, 0.9500246711335888-2, 0.25987829195170065-1].
?- X = 1000000, time(oracle_rands_explicit(_,X)).
% 4,000,001 inferences, 1.430 CPU in 1.459 seconds (98% CPU, 2797573 Lips)
?- X = 50000000, time(oracle_rands_explicit(_,X)).
ERROR: Stack limit (1.0Gb) exceeded
ERROR: Stack sizes: local: 1.0Gb, global: 7Kb, trail: 1Kb
ERROR: Stack depth: 10,321,204, last-call: 0%, Choice points: 6
ERROR: Possible non-terminating recursion: ...
Faster, and does not run out of stack space
Again, we just allow a head with no function symbols to make things explicit, but we move the recursive call to the end of the body, which apparently makes a difference!
% Same number of inferences as Slowest, i.e. 4 inferences per call
% (+ 1 at the end of recursion), but at HALF the time.
% Does not run out of stack space! Conclusion: this is tail-call-optimized.
oracle_rands_explicit_last_call(Out,Size) :-
Size>0, !,
NewSize is Size-1,
X is random_float,
Out = [X-Size|R],
oracle_rands_explicit_last_call(R,NewSize).
oracle_rands_explicit_last_call([],0).
?- oracle_rands_explicit_last_call(X,4).
X = [0.6450176209046125-4, 0.5605468429780708-3, 0.597052872950385-2, 0.14440970112076815-1].
?- X = 1000000, time(oracle_rands_explicit_last_call(_,X)).
% 4,000,001 inferences, 0.697 CPU in 0.702 seconds (99% CPU, 5739758 Lips)
?- X = 50000000, time(oracle_rands_explicit_last_call(_,X)).
% 200,000,001 inferences, 32.259 CPU in 32.464 seconds (99% CPU, 6199905 Lips)
Compact, less inferences, and does not run out of stack space
Here we allow function symbols in the head for more compact notation. Still naive recursion.
% Only 3 inferences per call (+ 1 at the end of recursion), but approx
% same time as "Faster".
% Does not run out of stack space! Conclusion: this is tail-call-optimized.
oracle_rands_compact([X-Size|R],Size) :-
Size>0, !,
NewSize is Size-1,
X is random_float,
oracle_rands_compact(R,NewSize).
oracle_rands_compact([],0).
?- oracle_rands_compact(X,4).
X = [0.815764980826608-4, 0.6516093608470418-3, 0.03206964297092248-2, 0.376168614426895-1].
?- X = 1000000, time(oracle_rands_compact(_,X)).
% 3,000,001 inferences, 0.641 CPU in 0.650 seconds (99% CPU, 4678064 Lips)
?- X = 50000000, time(oracle_rands_compact(_,X)).
% 150,000,001 inferences, 29.526 CPU in 29.709 seconds (99% CPU, 5080312 Lips)
Accumulator-based and unexpectedly runs out of (global) stack space
% Accumulator-based, 3 inferences per call (+ 1 at the end of recursion + 1 at ignition),
% but it is often faster than the compact version.
% Uses "global stack" as indicated in the "Stack limit (1.0Gb) exceeded"
% error at "Stack depth: 12,779,585":
% "Stack sizes: local: 1Kb, global: 0.9Gb, trail: 40.6Mb"
oracle_rands_acc(Out,Size) :- oracle_rands_acc(Size,[],Out).
oracle_rands_acc(Size,ThreadIn,ThreadOut) :-
Size>0, !,
NewSize is Size-1,
X is random_float,
oracle_rands_acc(NewSize,[X-Size|ThreadIn],ThreadOut).
oracle_rands_acc(0,ThreadIn,ThreadOut) :-
reverse(ThreadIn,ThreadOut).
?- oracle_rands_acc(X,4).
X = [0.7768407880604368-4, 0.03425412654687081-3, 0.6392634169514991-2, 0.8340458397587001-1].
?- X = 1000000, time(oracle_rands_acc(_,X)).
% 4,000,004 inferences, 0.798 CPU in 0.810 seconds (99% CPU, 5009599 Lips)
?- X = 50000000, time(oracle_rands_acc(_,X)).
ERROR: Stack limit (1.0Gb) exceeded
ERROR: Stack sizes: local: 1Kb, global: 0.9Gb, trail: 40.6Mb
ERROR: Stack depth: 12,779,585, last-call: 100%, Choice points: 6
ERROR: In:
ERROR: [12,779,585] user:oracle_rands_acc(37220431, [length:12,779,569], _876)
Addendum: Another version of the "compact" version.
Here we move the Size parameter to the first position, and do not use !. But indexing is a complex matter. Differences will probably be of note with many more clauses only.
oracle_rands_compact2(Size,[X-Size|R]) :-
Size>0,
NewSize is Size-1,
X is random_float,
oracle_rands_compact2(NewSize,R).
oracle_rands_compact2(0,[]).
Trying, with L instead of an anonymous variable, and L used after call.
X = 10000000, time(oracle_rands_compact2(X,L)),L=[].
% 30,000,002 inferences, 6.129 CPU in 6.159 seconds (100% CPU, 4894674 Lips)
X = 10000000, time(oracle_rands_compact(L,X)),L=[].
% 30,000,001 inferences, 5.865 CPU in 5.892 seconds (100% CPU, 5115153 Lips)
Maybe marginally faster. The above numbers vary a bit, one would really have to generate a full statistics over a hundred runs or so.
Does re-introducing the cut make it faster (it doesn't seem to make it slower)?
oracle_rands_compact3(Size,[X-Size|R]) :-
Size>0, !,
NewSize is Size-1,
X is random_float,
oracle_rands_compact3(NewSize,R).
oracle_rands_compact3(0,[]).
?- X = 10000000, time(oracle_rands_compact3(X,L)),L=[].
% 30,000,001 inferences, 5.026 CPU in 5.061 seconds (99% CPU, 5969441 Lips)
Can't say, really.
It all depends on the top-level shell and the actual interpretation of the _. Try
?- X = 50000000, time(oracle_rands_compact(L,X)),L=[].
instead, it will be more or less as bad as the accumulator version which has to produce the entire list first only to hand it over to reverse/2. To see this use
?- set_prolog_flag(trace_gc, true).
true.
?- X = 50000000, time(oracle_rands_compact(_,X)).
% GC: gained 0+0 in 0.001 sec; used 440+8; free 126,520+129,008
% GC: gained 0+0 in 0.000 sec; used 464+16; free 126,496+129,000
% GC: gained 0+0 in 0.000 sec; used 464+16; free 126,496+129,000
...
?- X = 50000000, time(oracle_rands_compact(L,X)),L=[].
% SHIFT: l:g:t = 0:1:0 ...l+g+t = 131072+262144+131072 (0.000 sec)
% GC: gained 0+0 in 0.002 sec; used 123,024+16; free 135,008+129,000
% SHIFT: l:g:t = 0:1:0 ...l+g+t = 131072+524288+131072 (0.000 sec)
% GC: gained 0+0 in 0.003 sec; used 257,976+24; free 262,200+128,992
% SHIFT: l:g:t = 0:0:1 ...l+g+t = 131072+524288+262144 (0.000 sec)
% SHIFT: l:g:t = 0:1:0 ...l+g+t = 131072+1048576+262144 (0.000 sec)
% GC: gained 0+0 in 0.007 sec; used 520,104+16; free 524,360+260,072
...
If we are at it, your _compact version can be accelerated by exchanging the arguments and removing the cut. Classic first argument indexing is capable of handling this situation avoiding any choice point. (SWI has WAM style 1st argument indexing plus a lesser version for multiple arguments, last time I checked)
This is the question.
Define a predicate sigma(N,S) such that S = 1+2+...+N. And remember every new intermediate result in the query. For example, after query sigma(3,S), it will store some thing like sigma(2,3),sigma(3,6) to database such that we needn't do duplicate and useless work later.
I tried the following method to solve it.
sigmares(1,1).
mysigma(N,A,Sum) :-
sigmares(N,SN),
Sum is SN+A,
!.
mysigma(N1,Acc,Sum) :-
N is N1-1,
A is Acc + N1,
mysigma(N,A,Sum),
assertz(sigmares(N1,Sum)). % <<<<<<<<<< This line doesn't work properly.
sigma(N,X) :-
mysigma(N,0,X).
There is some problem with assertz line. Since sum can be only initialized once which is the value of sum from 1 to N, sigma(2,6),sigma(3,6) for query sigma(3,S) will be inserted. Is there any other way to store new intermediate sigmares?
First, it's good coding style to always declare the dynamic predicates that your code uses using the standard dynamic/1 directive. Simply add at the beginning of the file:
:- dynamic(sigmares/2).
An interesting aspect of your definition of the mysigma/3 predicate is that it is a non tail-recursive with the consequence that it requires space linear on its inputs. But that allows it to cache all intermediate results as you intend. A fixed version of your code will be:
:- dynamic(sigma_cache/2).
sigma_cache(1, 1).
sigma(N, S) :-
sigma_cache(N, S),
!.
sigma(N, S) :-
N > 1,
M is N - 1,
sigma(M, SM),
S is SM + N,
assertz(sigma_cache(N, S)).
Sample call:
?- sigma(5, S).
S = 15.
?- listing(sigma_cache/2).
:- dynamic sigma_cache/2.
sigma_cache(1, 1).
sigma_cache(2, 3).
sigma_cache(3, 6).
sigma_cache(4, 10).
sigma_cache(5, 15).
true.
This alternative answer provides a solution based on the tabling mechanism found in some Prolog systems, including B-Prolog, Ciao, SWI-Prolog, XSB, and YAP:
:- table(sigma/2).
sigma(1, 1).
sigma(N, S) :-
N > 1,
M is N - 1,
sigma(M, SM),
S is SM + N.
Let's test it with the help of SWI-Prolog handy time/1 library predicate that reports the time and number of inferences taken to prove a goal:
?- time(sigma(5, S)).
% 166 inferences, 0.000 CPU in 0.006 seconds (2% CPU, 1238806 Lips)
S = 15.
?- time(sigma(5, S)).
% 5 inferences, 0.000 CPU in 0.000 seconds (68% CPU, 208333 Lips)
S = 15.
Note that I used a non tail-recursive definition for the sigma/2 predicate on purpose so that all intermediate results are cached (as per the requirements in your question). For example:
?- time(sigma(4, S)).
% 5 inferences, 0.000 CPU in 0.000 seconds (70% CPU, 217391 Lips)
S = 10.
You can see that, after the first call, the result is cached by the tabling mechanism, resulting in a much lower number of inferences when we repeat the query.
?- time(sigma(6, S)).
% 32 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 727273 Lips)
S = 21.
?- time(sigma(6, S)).
% 5 inferences, 0.000 CPU in 0.000 seconds (70% CPU, 217391 Lips)
S = 21.
Note again the number of inferences. The first query reuses the cached result for sigma(5, S) and caches the result for sigma(6, S), making the repeated query again faster as it just reuses the cached result.
I defined in Prolog a STRIPS Planner to solve logic problems. After a few tryouts with other simpler problems I set out to see if it could solve a more complex one. I gave him a STRIPS definition of the peg solitaire, the english version and considering we cant do diagonal moves and the last ball will end up in the center of the board and tried it, to which the program broke into a loop. Here's the problem: https://en.wikipedia.org/wiki/Peg_solitaire
Here's my solution:
%%%%%%%%%%%%%%%%%%%%%% PLAN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
accao(nome : move(Xi,Yi,Xf,Yf),
condicoes : [empty(Xf,Yf),ball(Xi,Yi), ball(Xm,Ym)],
efeitos : [ball(Xf,Yf), -ball(Xm,Ym),-ball(Xi,Yi), empty(Xi,Yi), empty(Xm,Ym), -empty(Xf,Yf)],
restricoes : [abs(Xf-Xi)+abs(Yf-Yi)=:=2, abs(Xf-Xi)*abs(Yf-Yi)=:=0, Xi=<Xm, Xm=<Xf, Yi=<Ym, Ym=<Yf]).
inicial([empty(5,5), ball(1,4), ball(1,5), ball(1,6),
ball(2,4), ball(2,5), ball(2,6),
ball(3,4), ball(3,5), ball(3,6),
ball(4,1), ball(4,2), ball(4,3),ball(4,4), ball(4,5), ball(4,6),ball(4,7), ball(4,8), ball(4,9),
ball(5,1), ball(5,2), ball(5,3),ball(5,4), ball(5,6),ball(5,7), ball(5,8), ball(5,9),
ball(6,1), ball(6,2), ball(6,3),ball(6,4), ball(6,5), ball(6,6),ball(6,7), ball(6,8), ball(6,9),
ball(7,4), ball(7,5), ball(7,6),
ball(8,4), ball(8,5), ball(8,6),
ball(9,4), ball(9,5), ball(9,6)]).
objectivos([ball(5,5), empty(1,4), empty(1,5), empty(1,6),
empty(2,4), empty(2,5), empty(2,6),
empty(3,4), empty(3,5), empty(3,6),
empty(4,1), empty(4,2), empty(4,3),empty(4,4), empty(4,5), empty(4,6),empty(4,7), empty(4,8), empty(4,9),
empty(5,1), empty(5,2), empty(5,3),empty(5,4), empty(5,6),empty(5,7), empty(5,8), empty(5,9),
empty(6,1), empty(6,2), empty(6,3),empty(6,4), empty(6,5), empty(6,6),empty(6,7), empty(6,8), empty(6,9),
empty(7,4), empty(7,5), empty(7,6),
empty(8,4), empty(8,5), empty(8,6),
empty(9,4), empty(9,5), empty(9,6)]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%% PRINT FUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%
printExec([]).
printExec([A,E|T]) :- write("Action performed: "),
write(A),nl,
write("Situation: "),
write(E),nl,
printExec(T).
writeExec([I|T]):- write("Initial Situation"),
write(I),nl,
printExec(T),
write("Goal: "),
objectivos(G),
write(G),
write(" satisfied."),nl.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%% AUXILIAR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%
member(E,[E|_]).
member(E,[_|T]):-member(E,T).
sub([],_).
sub([H|T],L):- member(H,L),
sub(T,L).
remove(_,[],[]):-!.
remove(E1, [E2|T], T):- E1 == E2, !.
remove(E,[H|T1],[H|T2]):- remove(E,T1,T2).
add(E,[],[E]):-!.
add(E1,[E2|T],[E1,E2|T]):- E1 \== E2, !.
add(E,[H|T1],[H|T2]):-add(E,T1,T2).
effects([],S,S).
effects([-H|Fx],S,N) :-!,
remove(H,S,NS),
effects(Fx,NS,N).
effects([H|Fx],S,N) :- !,
add(H,S,NS),
effects(Fx,NS,N).
restriction([]).
restriction([R|T]) :- R,
restriction(T).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% PLAN EXECUTE %%%%%%%%%%%%%%%%%%%%%%%%%%%
planExecute(P):-testPlan(P,E),writeExec(E),!.
satisfiedGoal(E):- objectivos(Fn),!,
sub(Fn,E).
testPlan(Plan,[I|Exec]) :- inicial(I),
testPlan(Plan,I,Exec,Fn),
satisfiedGoal(Fn).
testPlan([],Fn,[],Fn).
testPlan([H|T],S,[H,N|Exec],Fn) :- accao(nome:H, condicoes:C,efeitos:E, restricoes:R),
sub(C,S),
effects(E,S,N),
restriction(R),
testPlan(T,N,Exec,Fn).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% FIND PLAN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
plano(P) :- progressivePlan(P, 0).
progressivePlan(P, N) :- createPlan(P,_,0,N).
progressivePlan(P, N) :- \+ createPlan(P,_,0,N),
NewN is N + 1,
progressivePlan(P, NewN).
createPlan(Plan,[I|Exec],N,Max) :- inicial(I),
createPlan(Plan,I,Exec,Fn,N,Max),
satisfiedGoal(Fn).
createPlan([],Fn,[],Fn,Max,Max):- !.
createPlan([H|T],S,[H,N|Exec],Fn,Acc, Max) :- accao(nome:H, condicoes:C, efeitos:E, restricoes:R),
sub(C,S),
effects(E,S,N),
restriction(R),
NewAcc is Acc+1,
createPlan(T,N,Exec,Fn,NewAcc, Max).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%`
I've tried simplifying the goal by just doing one or two moves, which works for the one and when the two moves don't contradict each other, like moving a marble on through one that was already moved, entering the loop with two moves when they do, like with said objective:
objectivos([ball(4,5), empty(3,5), empty(5,5), empty(6,5)]).
I've tried tracing and debugging but I cant seem to find the issue, although I believe it to be located in the formulation of the problem as opposed to the Planner itself. Any Ideas?
There is at least one logical error in your code, and some simple performance tweaks are possible. This gives a partial solution to your problem.
First, for the logical error: The intended solution for the goal objectivos([ball(4,5), empty(3,5), empty(5,5), empty(6,5)]) seems to be the plan P = [move(3, 5, 5, 5), move(6, 5, 4, 5)]. But the second of these moves is not legal with your definition of restricoes: For this move you have Xi = 6, Xf = 4, and conditions requiring that 6 =< Xm and Xm <= 4, but this is impossible. The idea of these constraints is to ensure that ball(Xm,Ym) is between the other two balls in the move. Here is an alternative formulation that ensures this:
restricoes : [abs(Xf-Xi)+abs(Yf-Yi) =:= 2,
abs(Xf-Xi)*abs(Yf-Yi) =:= 0,
abs(Xf-Xm)+abs(Yf-Ym) =:= 1,
abs(Xi-Xm)+abs(Yi-Ym) =:= 1]
This also excludes a case that confused me before, when tracing the code: Previously it was legal to have ball(Xi,Yi) = ball(Xm,Ym).
Second, to improve performance, exchange the goals effects(E,S,N) and restriction(R) in the definition of createPlan/6. Previously you computed the effects of moves before checking their legality! Because most moves proposed by the planner are illegal, this wastes a lot of time.
Then, to make the whole thing nicer to use, you can change the definitions of plano/1 and createPlan/4 to:
plano(P) :-
length(P, PlanLength),
createPlan(P, _, 0, PlanLength).
createPlan(Plan,[I|Exec],N,Max) :- inicial(I),
N =< Max,
createPlan(Plan,I,Exec,Fn,N,Max),
satisfiedGoal(Fn).
This is simpler than the definition you had before, and it also behaves more nicely. We can pass in a complete plan to check whether it is legal, or just pass in a list of fixed length to ask what plans of that length exist:
?- P = [_,_], plano(P).
P = [move(3, 5, 5, 5), move(6, 5, 4, 5)] ;
false. % no more solutions
With your definition, this would go on looping and counting up the Max counter, searching for further solutions that cannot exist.
With this formulation we can switch to your big goal and try to search for a solution (this is partly specific to SWI-Prolog):
?- length(P, N), format('searching for solutions of length ~w~n', [N]), time(plano(P)).
searching for solutions of length 0
% 58 inferences, 0.000 CPU in 0.000 seconds (71% CPU, 2171959 Lips)
searching for solutions of length 1
% 9,709 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 9123980 Lips)
searching for solutions of length 2
% 79,789 inferences, 0.009 CPU in 0.009 seconds (100% CPU, 8778416 Lips)
searching for solutions of length 3
% 477,230 inferences, 0.051 CPU in 0.051 seconds (100% CPU, 9409315 Lips)
searching for solutions of length 4
% 3,412,088 inferences, 0.361 CPU in 0.361 seconds (100% CPU, 9453315 Lips)
searching for solutions of length 5
% 30,967,699 inferences, 3.503 CPU in 3.503 seconds (100% CPU, 8840598 Lips)
searching for solutions of length 6
I had to interrupt the search at this point, it becomes too slow. More tweaks are certainly possible, and I might keep looking at this.