Related
I am writing a prolog program with can perform Peano arithmetics.
I have standard definitions for natural numbers.
nat(n).
nat(s(N)) :-
nat(N).
Because I want to enumerate all possible relation of addition between natural numbers, I defined an auxiliary function (in order for defining total ordering over the set).
cmp_n(X, Y, lt) :-
nat(Y), % generate a stream : n s(n) s(s(n)) ...
cmp_n_lt_helper(X, Y). % gives all XS smaller than Y
cmp_n_lt_helper(s(X), s(Y)) :-
cmp_n_lt_helper(X, Y).
cmp_n_lt_helper(n, s(Y)) :-
nat(Y).
Then, I defined addition
% need to use a wrapper because I want to generate (n, n, n) first
% if I don't use this warper, it would start from (n, s(n), s(n))
add_n(X, Y, R) :-
nat(R), % same reason as above
cmp_n(X, R, lt),
add_n_helper(X, Y, R).
add_n_helper(s(X), Y, s(R)):-
add_n_helper(X, Y, R).
add_n_helper(n, Y, Y).
If I enumerate all possible relations over this definition of addition, it worked fine. And when outputting a finite set of answers, it can halt.
?- add_n(X, Y, R).
X = Y, Y = R, R = n ;
X = R, R = s(n),
Y = n ;
X = n,
Y = R, R = s(n) ;
X = R, R = s(s(n)),
Y = n ;
X = Y, Y = s(n),
R = s(s(n)) ;
X = n,
Y = R, R = s(s(n)) .
?- add_n(X, Y, s(s(s(s(n))))).
X = s(s(s(s(n)))),
Y = n ;
X = s(s(s(n))),
Y = s(n) ;
X = Y, Y = s(s(n)) ;
X = s(n),
Y = s(s(s(n))) ;
X = n,
Y = s(s(s(s(n)))) ;
false.
These worked fine.
However, if I do the regular forward evaluation,
?- add_n(s(s(s(n))), s(s(n)), R).
R = s(s(s(s(s(n)))))
this program cannot halt.
I am wondering : is there a way to
for any finite answer, give a finite result.
for any infinite answer, fix a specific valid answer, give this specified answer in finite time
As spot properly in the comments and by you as well, you've got a problem in a specific case, when X and Y are defined and R is not.
So let's just solve this case separately without the R generator in that case.
In my implementation (similar to yours)
nat(n).
nat(s(N)) :-
nat(N).
eq_n(n, n) :- !.
eq_n(s(X), s(Y)) :-
eq_n(X, Y), !.
leq_n(n, n).
leq_n(n, Y) :-
nat(Y).
leq_n(s(X), s(Y)) :-
leq_n(X, Y).
movel_n(X, n, X) :- !.
movel_n(X, s(Y), Z) :-
movel_n(s(X), Y, Z), !.
add_n(X, Y, R) :-
( ( var(X)
; var(Y)
),
nat(R),
leq_n(X, R),
leq_n(Y, R)
; \+ var(X),
\+ var(Y), !
),
movel_n(X, Y, Xn),
eq_n(Xn, R).
The most important part for you is the first big or statement of add_n/3.
We're checking there with the var/1 if the variables are instantiated.
If not, we're creating the variables generator,
otherwise, we're just going forward to calculations.
The following Prolog program defines a predicate fib/2 for computing the Fibonacci number of an integer in successor arithmetics:
fib(0, 0).
fib(s(0), s(0)).
fib(s(s(N)), F) :-
fib(N, F1),
fib(s(N), F2),
sum(F1, F2, F).
sum(0, N, N).
sum(s(N1), N2, s(S)) :-
sum(N1, N2, S).
It works with queries in this argument mode:
?- fib(s(0), s(0)).
true
; false.
It also works with queries in this argument mode:
?- fib(s(0), F).
F = s(0)
; false.
It also works with queries in this argument mode:
?- fib(N, F).
N = F, F = 0
; N = F, F = s(0)
; N = s(s(0)), F = s(0)
; N = s(s(s(0))), F = s(s(0))
; N = s(s(s(s(0)))), F = s(s(s(0)))
; …
But it exhausts resources with queries in this argument mode:
?- fib(N, s(0)).
N = s(0)
; N = s(s(0))
;
Time limit exceeded
How to implement the Fibonacci sequence in successor arithmetics for all argument modes?
This answer computes the fibonacci number "bottom up" using the two previous computed values, so that it will only make one recursive tail call:
fib(0, 0).
fib(s(0), s(0)).
fib(s(s(X)), F):-
fib(X, 0, s(0), F, F).
fib(0, F_2, F_1, _, F):-
sum(F_2, F_1, F).
fib(s(X), F_2, F_1, s(Y), F):-
sum(F_2, F_1, F_0),
fib(X, F_1, F_0, Y, F).
sum(0, Y, Y).
sum(s(X), Y, s(Z)):-
sum(X, Y, Z).
At least in SWI with default configuration it exhausts resources computing the fibonacci(37) building the addition term in sum/3.
The failure slice causing universal non-termination when the first argument of fib/2 is unbound is
fib(s(s(N)), F) :-
fib(N, F1),
false,
fib(s(N), F2),
sum(F1, F2, F).
The reason is that only the first argument is restricted in the recursive call fib(N, F1), so if it is unbound the restriction does not apply.
cTI proves that
fib(A,B)terminates_if b(A).
To allow universal termination when the first argument is unbound, one should restrict the second argument in the recursive call and therefore the second argument should be bound for the restriction to apply:
fib(N, F) :-
fib(N, F, F).
fib(0, 0, _).
fib(s(0), s(0), _).
fib(s(s(N)), F, s(X)) :-
fib(N, F1, X),
fib(s(N), F2, X),
sum(F1, F2, F).
sum(0, N, N).
sum(s(N1), N2, s(S)) :-
sum(N1, N2, S).
cTI proves that
fib(A,B)terminates_if b(A);b(B).
So now the query terminates:
?- fib(N, s(0)).
N = s(0)
; N = s(s(0))
; false.
The naive recursive implementation of fib/2 provided in my other answer has a time complexity of O(φ^N) where φ is the golden ratio i.e. exponential time. Here is a tail recursive implementation of fib/2 which has a time complexity of O(N) i.e. linear time:
fib(N, F) :-
fib(N, 0, s(0), F, F).
fib(0, A, _, A, _).
fib(s(0), _, B, B, _).
fib(s(s(N)), A, B, s(F), s(X)) :-
sum(A, B, S),
fib(s(N), B, S, s(F), X).
sum(0, N, N).
sum(s(N1), N2, s(S)) :-
sum(N1, N2, S).
Sample queries
The most general query (all arguments are free):
?- fib(N, F).
F = N, N = 0
; F = N, N = s(0)
; F = s(0), N = s(s(0))
; F = s(s(0)), N = s(s(s(0)))
; F = s(s(s(0))), N = s(s(s(s(0))))
; F = N, N = s(s(s(s(s(0)))))
; F = s(s(s(s(s(s(s(s(0)))))))), N = s(s(s(s(s(s(0))))))
; F = s(s(s(s(s(s(s(s(s(s(s(s(s(0))))))))))))), N = s(s(s(s(s(s(s(0)))))))
; …
Queries with a first argument that is free:
?- fib(N, 0).
N = 0
; false.
?- fib(N, s(0)).
N = s(0)
; N = s(s(0))
; false.
?- fib(N, s(s(0)).
N = s(s(s(0)))
; false.
?- fib(N, s(s(s(0))).
N = s(s(s(s(0))))
; false.
?- fib(N, s(s(s(s(s(0)))))).
N = s(s(s(s(s(0)))))
; false.
?- fib(N, s(s(s(s(s(s(s(s(0))))))))).
N = s(s(s(s(s(s(0))))))
; false.
Queries with a second argument that is free:
?- fib(0, F).
F = 0
; false.
?- fib(s(0), F).
F = s(0)
; false.
?- fib(s(s(0)), F).
F = s(0)
; false.
?- fib(s(s(s(0))), F).
F = s(s(0))
; false.
?- fib(s(s(s(s(0)))), F).
F = s(s(s(0)))
; false.
?- fib(s(s(s(s(s(0))))), F).
F = s(s(s(s(s(0)))))
; false.
The following Prolog program defines a predicate fact/2 for computing the factorial of an integer in successor arithmetics:
fact(0, s(0)).
fact(s(X), Y) :-
fact(X, Z),
prod(s(X), Z, Y).
prod(0, _, 0).
prod(s(U), V, W) :-
sum(V, X, W),
prod(V, U, X).
sum(0, Y, Y).
sum(s(X), Y, s(Z)) :-
sum(X, Y, Z).
It works with queries in this argument mode:
?- fact(s(0), s(0)).
true
; false.
It also works with queries in this argument mode:
?- fact(s(0), Y).
Y = s(0)
; false.
It also works with queries in this argument mode:
?- fact(X, Y).
X = 0, Y = s(0)
; X = Y, Y = s(0)
; X = Y, Y = s(s(0))
; X = s(s(s(0))), Y = s(s(s(s(s(s(0))))))
; …
But it exhausts resources with queries in this argument mode:
?- fact(X, s(0)).
X = 0
; X = s(0)
;
Stack limit (0.2Gb) exceeded
Stack sizes: local: 4Kb, global: 0.2Gb, trail: 0Kb
Stack depth: 2,503,730, last-call: 100%, Choice points: 13
In:
[2,503,730] sum('<garbage_collected>', _1328, _1330)
[38] prod('<garbage_collected>', <compound s/1>, '<garbage_collected>')
[33] fact('<garbage_collected>', <compound s/1>)
[32] fact('<garbage_collected>', <compound s/1>)
[31] swish_trace:swish_call('<garbage_collected>')
How to implement the factorial sequence in successor arithmetics for all argument modes?
The first question must be why? A failure-slice helps to understand the problem:
fact(0, s(0)) :- false.
fact(s(X), Y) :- fact(X, Z), false, prod(s(X), Z, Y).
This fragment alone terminates only if the first argument is given. If it is not, then there is no way to prevent non-termination, as Y is not restricted in any way in the visible part. So we have to change that part. A simple way is to observe that the second argument continually increases. In fact it grows quite fast, but for the sake of termination, one is enough:
fact2(N, F) :-
fact2(N, F, F).
fact2(0, s(0), _).
fact2(s(X), Y, s(B)) :- fact2(X, Z, B), prod(s(X), Z, Y).
And, should I add, this can be even proved.
fact2(A,B)terminates_if b(A);b(B).
% optimal. loops found: [fact2(s(_),s(_))]. NTI took 0ms,73i,73i
But, there is a caveat...
If only F is known, the program will now require temporally space proprotional to |F|! That is not an exclamation point but a factorial sign...
I think you can use cut to avoid backtracking when the second argument is a ground term.
fact(0, s(0)).
fact(s(X), Y) :-
fact(X, Z),
prod(s(X), Z, W),
(ground(Y) ->
!,
Y = W
; Y = W).
prod(0, _, 0).
prod(s(U), V, W) :- sum(V, X, W), prod(V, U, X).
sum(0, Y, Y).
sum(s(X), Y, s(Z)) :- sum(X, Y, Z).
Examples:
?- fact(N, 0).
false.
?- fact(N, s(s(s(0)))).
false.
?- fact(X, s(0)).
X = 0
; X = s(0)
; false.
?- fact(s(s(s(0))), s(s(s(s(s(s(0))))))).
true
; false.
?- fact(s(s(s(0))), s(s(s(s(s(0)))))).
; false.
?- fact(s(s(s(0))), Y).
Y = s(s(s(s(s(s(0))))))
; false.
?- fact(X, Y).
X = 0, Y = s(0)
; X = Y, Y = s(0)
; X = Y, Y = s(s(0))
; …
?- fact(s(s(X)), s(s(Y))).
X = Y, Y = 0
; X = s(0), Y = s(s(s(s(0))))
; …
I am trying to implement exponentiation with the code below, but a simple query like 2^1 (ex(s(s(0)), s(0), Z).) hangs forever.
nat(0).
nat(s(X)) :- nat(X).
su(0, X, X) :- nat(X).
su(s(X), Y, s(Z)) :- su(X, Y, Z).
mu(0, _, 0).
mu(s(X), Y, Z) :- su(Y, A, Z), mu(X, Y, A).
ex(_, 0, s(0)).
ex(X, s(Y), Z) :- mu(X, A, Z), ex(X, Y, A).
As far as I can see, it is not efficient, because the mu/3 is called with two free variables. Indeed:
ex(X, s(Y), Z) :- mu(X, A, Z), ex(X, Y, A).
Both A and Z are unknown at that moment (I have put them in boldface).
Now your mu/2 is not capable of handling this properly. If we query mu/3 with mu(s(0), A, Z), we get:
?- mu(s(0), A, Z).
A = Z, Z = 0 ;
ERROR: Out of global stack
So it got stuck in infinite recursion as well.
This is due to the fact that it will tak the second clause of mu/3, and:
mu(s(X), Y, Z) :- su(Y, A, Z), mu(X, Y, A).
So su/3 is called with three unknown variables. The effect of this is that su/3 can keep proposing values "until the end of times":
?- su(A, B, C).
A = B, B = C, C = 0 ;
A = 0,
B = C, C = s(0) ;
A = 0,
B = C, C = s(s(0)) ;
A = 0,
...
even if the recursive mu(X, Y, A) rejects all these proposals, su/3 will never stop proposing new solutions.
Therefore it might be better to keep that in mind when we design the predicates for mu/3, and ex/3.
We can for example use an accumulator here that accumulates the values, and returns the end product. The advantage of this, is that we work with real values when we make the su/3 call, like:
mu(A, B, C) :-
mu(A, B, 0, C).
mu(0, _, 0, S, S).
mu(s(X), Y, I, Z) :-
su(Y, I, J),
mu(X, Y, J, Z).
Now if we enter mu/3 with only the first parameter fixed, we see:
?- mu(s(0), X, Y).
X = Y, Y = 0 ;
X = Y, Y = s(0) ;
X = Y, Y = s(s(0)) ;
X = Y, Y = s(s(s(0))) ;
...
?- mu(s(s(0)), X, Y).
X = Y, Y = 0 ;
X = s(0),
Y = s(s(0)) ;
X = s(s(0)),
Y = s(s(s(s(0)))) ;
X = s(s(s(0))),
Y = s(s(s(s(s(s(0)))))) ;
...
...
So that means that we now at least do not get stuck in a loop for mu/3 with only the first parameter fixed.
We can use the same strategy to define an ex/3 predicate:
ex(X, Y, Z) :-
ex(X, Y, s(0), Z).
ex(X, 0, Z, Z).
ex(X, s(Y), I, Z) :-
mu(X, I, J),
ex(X, Y, J, Z).
We then manage to calculate exponents like 21 and 22:
?- ex(s(s(0)), s(0), Z).
Z = s(s(0)) ;
false.
?- ex(s(s(0)), s(s(0)), Z).
Z = s(s(s(s(0)))) ;
false.
Note that the above has still some flaws, for example calculating for which powers the value is 4 will still loop:
?- ex(X, Y, s(s(s(s(0))))).
ERROR: Out of global stack
By rewriting the predicates, we can avoid that as well. But I leave that as an exercise.
I have a knowledge base with the following:
numeral(0).
numeral(s(X)) :- numeral(X).
numeral(X+Y) :- numeral(X), numeral(Y).
add(0,X,X).
add(s(X),Y,s(Z)) :- add(X,Y,Z).
add2(W+X,Y+Z,R) :- add(W,X,A),add(Y,Z,T),add2(A,T,R).
add2(X+Y,Z,R) :- add(X,Y,A),add2(A,Z,R).
add2(X,Y+Z,R) :- add(Y,Z,A),add2(X,A,R).
add2(X,Y,R) :- add(X,Y,R).
which evaluates correctly queries such as:
?- add2(s(0)+s(s(0)), s(s(0)), Z).
Z = s(s(s(s(s(0)))))
?- add2(0, s(0)+s(s(0)), Z).
Z = s(s(s(0)))
?- add2(s(s(0)), s(0)+s(s(0)), Z).
Z = s(s(s(s(s(0)))))
However the following query is evaluated to:
?- add2(s(0)+s(0), s(0+s(s(0))), Z).
Z = s(s(s(0+s(s(0))))) .
But the required output is:
?- add2(s(0)+s(0), s(0+s(s(0))), Z).
Z = s(s(s(s(s(0)))))
I know the issue is with the line:
add2(W+X,Y+Z,R) :- add(W,X,A),add(Y,Z,T),add2(A,T,R).
But i just can't figure it out. Any help would be appreciated!
I think you make the problem more complex by handling the cases with an add2/3 predicate. You first need to resolve the structure of the first two arguments to something of the shape s(s(...s(0)...)).
In order to do this, we can make an resolve/2 function that looks for (+)/2 terms and recursively works with add/3:
resolve(0,0).
resolve(s(X),s(Y)) :-
resolve(X,Y).
resolve(X+Y,Z) :-
resolve(X,RX),
resolve(Y,RY),
add(RX,RY,Z).
So now for a grammar:
E -> 0
E -> s(E)
E -> E + E
resolve/2 will convert this to a grammar with:
E -> 0
E -> s(E)
For example:
?- resolve(s(0)+s(0),X).
X = s(s(0)).
?- resolve(s(0+s(s(0))),X).
X = s(s(s(0))).
And now our add2/3 predicate will first resolve/2 the operands, and then add these together:
add2(A,B,C) :-
resolve(A,RA),
resolve(B,RB),
add(RA,RB,C).
The sample queries you then write resolve to:
?- add2(s(0)+s(s(0)), s(s(0)), Z).
Z = s(s(s(s(s(0))))).
?- add2(0, s(0)+s(s(0)), Z).
Z = s(s(s(0))).
?- add2(s(s(0)), s(0)+s(s(0)), Z).
Z = s(s(s(s(s(0))))).
?- add2(s(0)+s(0), s(0+s(s(0))), Z).
Z = s(s(s(s(s(0))))).