Prolog code calculating factorial - prolog

I'm new to Prolog and I'm trying to write a piece of code that calculates factorial of a number.
This code works fine:
fact(0,1).
fact(N, R) :- N > 0, N1 is N - 1, fact(N1, R1), R is R1 * N.
But this one doesn't:
fact(0, 1).
fact(N, R) :- N > 0, fact(N - 1, R1), R is R1 * N.
Can someone please explain?

The issue is that prolog primarily uses unification to do computation. To get it to do arithmetic operations you need to tell it to do so explicitly using the is operator.
So, in your first program you explicitly tell it to perform subtraction with the clause N1 is N - 1, so that works as expected.
But in your second program you are not asking for arithmetic computation, but unification, when you wrote fact(N - 1, R1).
If I had the fact fact(5 - 1, foo). defined, then I could query for ?- fact(N - 1, Y), write([N, Y]). and prolog would happily unify N with 5 and Y with foo. This query would output [5, foo].
So, to go one step further, if I had the fact fact(foo - bar). then the query ?- fact(X - Y), write([X, Y]). would happily unify and return [foo, bar]. The - doesn't denote subtraction - it's part of the structure of the fact being represented.

When passing around arithmetic expressions (instead of numbers), you need to evaluate expressions at certain times.
Arithmetic operators like (>)/2 automatically do that, so the goal 1 > (0+0) succeeds, just like 1 > 0 does.
Implicit unification (in clause heads) and explicit unification with (=)/2 goals expresses equality of arbitrary Prolog terms, not just arithmetic expressions. So the goal 0 = 0 succeeds, but 0 = (1-1) fails.
With arithmetic equality (=:=)/2, both 0 =:= 0 and 0 =:= (1-1) succeed.
In your second definition of fact/2, you could make the first clause more general by writing fact(N,1) :- N =:= 0. instead of fact(0,1).. As an added bonus, you could then run queries like ?- fact(5+5,F). :)

Related

Different ways of expressing collatz conjecture in prolog fail

I'm learning prolog using SWI Prolog and the tutorial here. I find that if I express the collatz conjecture exactly like they do in the video, it works as long as I replace #= with is which I'm guessing is a swipl vs scryer-prolog difference. But if I tweak the definition at all it seems to break, either with an error or incorrect conclusions. Why do my alternative definitions fail? Code:
use_module(library(clpfd)).
%% Does work, collatz_next(A, 1) gives A=2
collatz_next(N0, N) :-
N0 is 2*N.
collatz_next(N0, N) :-
N0 is 2*_ + 1,
N is 3*N0 + 1.
%% Doesn't work, collatz_next(A, 1) gives false
%% collatz_next(N0, N) :- ((N0 mod 2) is 0),((N0 / 2) is N).
%% collatz_next(N0, N) :- ((N0 mod 2) is 1),((N0 * 3 + 1) is N).
%% Doesn't work, collatz_next(A, 1) gives false
%% collatz_next(N0, N) :- ((N0 mod 2) is 0),(N0 is 2*N).
%% collatz_next(N0, N) :- ((N0 mod 2) is 1),((N0 * 3 + 1) is N).
%% Doesn't work
%% "Arguments are not sufficiently instantiated"
%% collatz_next(N0, N) :-
%% N0 / 2 is N.
%% collatz_next(N0, N) :-
%% N0 is 2*_ + 1,
%% N is 3*N0 + 1.
As brebs already wrote in the comments, you have to include the line :- use_module(library(clpfd)). if you are using SWI-Prolog. However, if you are using Scryer Prolog the library is called clpz, so you have to include the line :- use_module(library(clpz)).. The predicate collatz_next/2 (from the linked video) works with both Prologs as described in the video if you use the respective libraries (I tested it with Scryer Prolog version 0.9.0. and SWI-Prolog version 8.4.2, both 64bit on a linux machine). Since it has not been mentioned in the comments yet, I would also refer you to the description of CLP(FD) and CLP(Z) in The Power of Prolog.
Now to your question about the failing alternatives. The built-in is/2 is true if the expression on the right hand side evaluates to the number on the left hand side. If the left hand side is an uninstantiated variable, the variable contains the value of the expression on the right hand side after the call of the goal. In order for this to succeed all variables in the expression on the right hand side need to be instatiated. Consider the following examples:
?- 3 is 2+1.
true.
?- X is 2+1.
X = 3.
?- 3 is X+1.
ERROR: Arguments are not sufficiently instantiated
...
?- 3 is 2+X.
ERROR: Arguments are not sufficiently instantiated
...
Furthermore if the left hand side is instantiated with a float rather than with an integer is/2 will fail:
?- 3.0 is 2+1.
false.
Now that we covered some basics let's take a look at your first predicate:
%% Does work, collatz_next(A, 1) gives A=2
collatz_next(N0, N) :-
N0 is 2*N.
collatz_next(N0, N) :-
N0 is 2*_ + 1,
N is 3*N0 + 1.
Let's observe that, while your given example produces a correct answer, after pressing the ;-key it throws an instantiation error:
?- collatz_next(A, 1).
A = 2 ;
ERROR: Arguments are not sufficiently instantiated
...
What's going on here? You're posting the query collatz_next(A, 1)., hence the variable N0 in the head of your predicate is unified with the variable A and the variable N is unified with 1. With these unifications the only goal in the first rule, N0 is 2*N, now becomes A is 2*1. That yields the answer A = 2. Prolog now tries the second rule of collatz_next/2, where the first goal, N0 is 2*_ + 1 now becomes A is 2*_ + 1. Here the right hand side still contains a variable (_), hence the expression is not sufficiently instatiated to be evaluated thus Prolog throws an instantiation error.
Now let's try to use the predicate the other way around. As you can see in the Youtube-video, if N0=5 then the expected answer is N=16. However, if you query that with your predicate you get no answer and an instantiation error:
?- collatz_next(5, N).
ERROR: Arguments are not sufficiently instantiated
...
Looking at your definition of collatz_next/2 we can observe that the variable N0 in the head of the rule is unified with 5, while the second argument N remains uninstantiated. The single goal in the first rule, N0 is 2*N, becomes 5 is 2*N hence the instantiation error due to the variable on the right hand side.
Note, that the video is also showing that the most general query :- collatz_next(N0,N). is still producing answers due to the use of CLP(FD)/CLP(Z), while your version, using is/2, is again producing an instantiation error.
The next two versions of collatz_next/2 you posted (the ones with the comment %% Doesn't work, collatz_next(A, 1) gives false) fail with the first goal in the rules. Since you query :- collatz_next(A, 1). the variable N0 in the head of the rules is unified with the variable A, so the first goal in all four rules becomes ((A mod 2) is 0) and ((A mod 2) is 1) respectively. If you try these goals as queries the answer is false:
?- ((A mod 2) is 0).
false.
?- ((A mod 2) is 1).
false.
And since the first goal of the rule fails, Prolog won't even try the second goal because once you have a false in a (chain of) conjunction(s) it cannot yield true. This is the case for both rules of both predicates hence the answer to your queries is false. If you, on the other hand, try to exchange the left hand sides of is/2 with the right hand sides you will get an instantiation error:
?- (0 is (A mod 2)).
ERROR: Arguments are not sufficiently instantiated
...
?- (1 is (A mod 2)).
ERROR: Arguments are not sufficiently instantiated
...
Maybe think about it this way: What can you expect to happen if you try to evaluate an uninstantiated variable modulo an actual number? One reasonable expectation would be to get some sort of feedback stating that it can't be done.
Another reasonable point of view would be to expect Prolog to propagate the posted goal as a constraint until it can (hopefully) be solved at a later time. This is basically what CLP(FD)/CLP(Z) does (see also the section on Constraint propagation in CLP(FD) and CLP(Z) in The Power of Prolog. Just try the above queries with (#=)/2:
?- ((A mod 2) #= 0).
A mod 2#=0. % residual goal
?- ((A mod 2) #= 1).
A mod 2#=1. % residual goal
?- (0 #= (A mod 2)).
A mod 2#=0. % residual goal
?- (1 #= (A mod 2)).
A mod 2#=1. % residual goal
As you can see, the posted constraints are now propagated and appear as residual goals at the end of the deduction since in these cases, with the queries just consisting of those single goals, they cannot be further resolved.
The last version you posted (marked with the comment %% Doesn't work) has the left hand side and the right hand side of is/2 the wrong way around in the single goal of the first rule, as pointed out by TessellatingHeckler in the comments. But even if you exchange them you will get an instantiation error unless the variable N0 is instantiated. But even then you will still get an instantiation error once Prolog tries the second rule because its first goal N0 is 2*_ + 1 contains a variable _ that is always uninstantiated:
?- N0 is 2*_ + 1.
ERROR: Arguments are not sufficiently instantiated
...
?- 1 is 2*_ + 1.
ERROR: Arguments are not sufficiently instantiated
...
The bottom line is: If you want to use low-level predicates like is/2 you have to be aware of their limitations. If you want to declaratively reason over integers you can't easily get around CLP(FD)/CLP(Z). And if you decide to use a predicate like collatz_next/2 as presented in the video, you can't exchange CLP(FD)/CLP(Z)-constraints one-to-one for low-level predicates like is/2 and expect the same results.

Reasoning through a program in Prolog

I am attempting a past paper question for a Prolog exam. I drew a 'tree' for how I believed Prolog ought to behave given the program and a certain goal. However, Prolog does not behave as I expected, and given a query for which I believed it would return 'true', it actually returned 'false'.
Here is my program:
sum(Term,N) :- Term = 0, N = 0.
sum(Term,N) :- Term = f(M,Subterm), number(M), sum(Subterm,N-M).
My query and search tree are as follows (goals are bracketed and in bold):
[ sum(f(1,0),1) ]
Using Rule 1, let Term = 0, N = 0, tries to unify [ 1 = 0, 1 = 0 ] fail.
Redo: using Rule 2, let Term = f(1,0), N=1 [ f(1,0) = f(M,Subterm), number(M), sum(Subterm,1-1) ]
Unifying, let M=1 and Subterm=0 [ number(1), sum(0,0) ]
Using Rule 1, this should succeed. However (SWI) Prolog says 'false'.
If someone can point out to me why my reasoning is flawed (and how I can learn from this in future), I would be very grateful.
Since your program is almost a pure1 one, you can locate the error in a systematic manner without using a debugger. The idea is to generalize your program by removing goals, one-by-one. I came up with the following pure generalization which I obtained by "commenting" out some goals like so:
:- op(950, fy, *).
*(_).
sum(Term,N) :-
Term = 0,
N = 0.
sum(Term,N) :-
* Term = f(M,Subterm),
* number(M),
sum(Subterm,N-M).
?- sum(Term, N).
Term = 0, N = 0
; false.
Also the query above is more general than yours. This is a very useful technique in Prolog: Instead of thinking about concrete solutions, we
first let Prolog do all the work for us.
The answer was quite clear: There is exactly one solution to this relation, even if the relation is now generalized.
So the problem must be somewhere in the remaining visible part. Actually, it's the -. Why not write instead:
:- use_module(library(clpfd)).
sum(0, 0).
sum(Term, N0) :-
Term = f(M, Subterm),
N0 #= M+N1,
sum(Subterm, N1).
I find that program much easier to understand. If I read a name sum, I immediately look for a corresponding +. Of course, if you insist, you could write N0-M #= N1 instead. It would be exactly the same, except that this requires a bit more thinking.
Fine print you don't need to read
1) Your original program used number/1 which is not pure. But since the problem persisted by removing it, it did not harm our reasoning.
To be more accurate, the first rule tries to unify f(1,0) = 0 and 1 = 0, which of course fails.
Analysis of rule 2 is also incorrect. Partly, it's because Prolog does not evaluate arithmetic expressions inline. The term N-M is just a term (short-hand for '-'(N, M). It does not result in M being subtracted from M unless the evaluation is done explicitly via is/2 or an arithmetic comparison (e.g., =:=/2, =</2, etc).
The analysis of rule 2 would go as follows. Step 5 is where your logic breaks down due to the above.
Call sum(f(1,0), 1) results in Term = f(1,0) and N = 1.
In rule 2, Term = f(M, Subterm) becomes f(1,0) = f(M, Subterm) which results in M = 1 and Subterm = 0.
number(N) becomes number(1) and succeeds (since 1 is a number)
The call sum(Subterm, N-M) becomes sum(0, 1-1).
Prolog matches sum(0, 1-1) with the head of rule 1 sum(Term, N) :- Term = 0, N = 0., but it fails because 1-1 = 0 (which is the same as '-'(1, 1) = 0 unification fails.
Prolog matches sum(0, 1-1) with the head of rule 2, and unifies Term = 0 and N = 1-1 (or N = '-'(1, 1)).
Term = f(M, Subterm) becomes 0 = f(M, Subterm) which fails because 0 cannot match the term f(M, Subterm).
No more rules to attempt, so the predicate call fails.
The easy fix here is a common, basic Prolog pattern to use a new variable to evaluate the expression explicitly:
sum(Term,N) :-
Term = f(M,Subterm),
number(M),
R is N - M,
sum(Subterm, R).
You can also tidy up the code quite a bit by unifying in the heads of the clauses. So the clauses could be rewritten:
sum(0, 0).
sum(f(M, Subterm), N) :-
number(N),
R is N - M,
sum(Subterm, R).
EDIT: My answer is intended to guide you through a walk through of your existing logic. Other than correcting the misunderstanding regarding expression evaluation, I did not analyze your solution for overall correctness.

Prolog converting text to number and doing arithmatic operations

I working in prolog for first time.
I am trying to convert operations in text.
Such as,
THREE + THREE = SIX
should return true.
I tried this.
I am getting error on last line and when I try add(ONE,ONE,TWO) it returns false instead of true.
numericValue(ONE, 1).
numericValue(TWO, 2).
numericValue(THREE, 3).
numericValue(FOUR, 4).
numericValue(FIVE, 5).
numericValue(SIX, 6).
numericValue(SEVEN, 7).
numericValue(EIGHT, 8).
numericValue(ZERO, 0).
numericValue(NINE, 9).
add(num1,num2,num3):-
numericValue(num1,a),
numericValue(num2,b),
numericValue(num3,c),
(c =:= a+b -> true ; false).
istBiggerThen(XinEng,YinEng) :-
numericValue(XinEng, X),
numericValue(YinEng, Y),
( X < Y -> true ; false).
A + B = C :- add(A,B,C).
Error on last line is
ERROR: /home/name/prolog_examples/crypt.pl:24:
No permission to modify static procedure `(=)/2'
literals (lower-case) vs. Variabls (upper-case):
as #lurker pointed out, you have your atoms and variables mixed up. So your facts should look something like this:
text_to_number(one, 1).
text_to_number(two, 2).
text_to_number(three, 3).
%% etc...
while your rules will need to use variables, like so:
add(A_Text, B_Text, C_Text) :-
text_to_number(A_Text, A_Num),
text_to_number(B_Text, B_Num),
C_Num is A_Num + B_Num,
text_to_number(C_Text, C_Num).
bigger_than(A_Text, B_Text) :-
text_to_number(A_Text, A_Num),
text_to_number(B_Text, B_Num),
A_Num > B_Num.
The reason reason why add(ONE, ONE, TWO) turns out false is because your original rule for add/3 only defines relationships between the atoms num1, num2, num3, a, b, c. When you query add(ONE, ONE, TWO) Prolog tries to unify the variables with the atoms in the head of your rule, which is add(num1, num2, num3). Because you have ONE as the first and second argument of your query, this unification is impossible, since ONE = ONE but num1 \= num2. As there are no further rules or facts for add/3, the query simply returns false.
Using the pattern (|Condition| -> true ; false):
Statements in the body of a clause (i.e., to the right of the :- operator) is evaluated to be either true or false, so you will almost never need to use the pattern (|Condition| -> true ; false). E.g. C_Num is A_Num + B_Num is true iff C_Num can be unified with the sum of A_Num and B_Num, or else it is false, in which case Prolog will start back tracking.
Using =:=/2 vs. is/2:
=:=/2 checks for the equality of its first argument with the value of its second argument, which can be an arithmetical expression that can be evaluated using is/2. Query ?- X =:= 2 + 2 and you'll get an instantiation error, because =:=/2 cannot compare a free variable to a mathematical expression. is/2, on the other hand, unifies the variable on the left with the value of the expression on the right: ?- X is 2 + 2. X = 4.
Your use of =:=/2 would work (provided you straightened out the variable-atom thing), but your rule describes an inefficient and roundabout solution for the following reason: since numericValue(Num3,C) precedes evaluation of the arithmetic, Prolog will first unify numericValue(Num3,C) with the first fitting fact, viz. numericValue(one, 1) then test if 1 =:= A + B. When this fails, Prolog will unify with the next fact numericValue(two, 2) then test if 2 =:= A + B, then the next... until it finally happens upon the right value. Compare with my suggested rule: the numeric values A_Num and B_Num are summed with C_Num is A_Num + B_Num, unifying C_Num with the sum. Then Prolog unifies text_to_number(C_Text, C_Num) with the single fitting fact that has the appropriate value for C_Num.
Defining operators:
When a term appears on the right of a :-, or on the top level of the program, is being defined. However, you cannot simply redefine predicates (it can be done, but requires some bookkeeping and special declarations. Cf., dynamic/1). Moreover, you wouldn't want to redefine core terms like +/2 and =/2. But you can define your own predicates with relative ease. In fact, going crazy with predicate definitions is one of my favorite idle things to do with Prolog (though I've read cautions against using unnecessary operators in practice, since it makes your code recondite).
Operators are declared using op/3 in a directive. It has the signature op(+Precedence, +Type, :Name) (Cf., the SWI-Prolog documentation):
:- op(200, xfx, user:(++)).
:- op(300, yfx, user:(=::=)).
A ++ B =::= C :- add(A, B, C).
In action:
?- one ++ two =::= X.
X = three.

Why is this elementary Prolog predicate not stopping execution?

I want to write a predicate that determines if a number is prime or not. I am doing this by a brute force O(sqrt(n)) algorithm:
1) If number is 2, return true and do not check any more predicates.
2) If the number is even, return false and do no more checking predicates.
3) If the number is not even, check the divisors of the number up to the square root. Note that
we need only to check the odd divisors starting at 3 since if we get to this part of
the program the number is not even. Evens were eliminated in step 2.
4) If we find an even divisor, return false and do not check anything else.
5) If the divisor we are checking is larger than the square root of the number,
return true, we found no divisors. Do no more predicate checking.
Here is the code I have:
oddp(N) :- M is N mod 2, M = 1.
evenp(N) :- not(oddp(N)).
prime(2) :- !.
prime(X) :- X < 2, write_ln('case 1'), false, !.
prime(X) :- evenp(X), write_ln('case 2'), false, !.
prime(X) :- not(evenp(X)), write_ln('calling helper'),
prime_helper(X,3).
prime_helper(X, Divisor) :- K is X mod Divisor, K = 0,
write_ln('case 3'), false, !.
prime_helper(X, Divisor) :- Divisor > sqrt(X),
write_ln('case 4'), !.
prime_helper(X, Divisor) :- write_ln('case 5'),
Temp is Divisor + 2, prime_helper(X,Temp).
I am running into problems though. For example, if I query prime(1). the program is still checking the divisors. I thought that adding '!' would make the program stop checking if the prior conditions were true. Can someone tell me why the program is doing this? Keep in mind I am new at this and I know the code can be simplified. However, any tips would be appreciated!
#Paulo cited the key issues with the program that cause it to behave improperly and a couple of good tips. I'll add a few more tips on this particular program.
When writing a predicate, the focus should be on what's true. If your
predicate properly defines successful cases, then you don't need to explicitly
define the failure cases since they'll fail by default. This means your statements #2 and #4 don't need to be specifically defined as clauses.
You're using a lot of cuts which is usually a sign that your program
isn't defined efficiently or properly.
When writing the predicates, it's helpful to first state the purpose in logical language form (which you have done in your statements 1 through 5, but I'll rephrase here):
A number is prime if it is 2 (your statement #1), or if it is odd and it is not divisible by an odd divisor 3 or higher (your statement #3). If we write this out in Prolog, we get:
prime(X) :- % X is prime if...
oddp(X), % X is odd, AND
no_odd_divisors(X). % X has no odd divisors
prime(2). % 2 is prime
A number X is odd if X module 2 evaluates to 1.
oddp(X) :- X mod 2 =:= 1. % X is odd if X module 2 evaluates to 1
Note that rather than create a helper which essentially fails when I want success, I'm going to create a helper which succeeds when I want it to. no_odd_divisors will succeeds if X doesn't have any odd divisors >= 3.
A number X has no odd divisors if it is not divisible by 3, and if it's not divisible by any number 3+2k up to sqrt(X) (your statement #5).
no_odd_divisors(X) :- % X has no odd divisors if...
no_odd_divisors(X, 3). % X has no odd divisors 3 or above
no_odd_divisors(X, D) :- % X has no odd divisors D or above if...
D > sqrt(X), !. % D is greater than sqrt(X)
no_odd_divisors(X, D) :- % X has no odd divisors D or above if...
X mod D =\= 0, % X is not divisible by D, AND
D1 is D + 2, % X has no odd divisors D+2 or above
no_odd_divisors(X, D1).
Note the one cut above. This indicates that when we reach more than sqrt(X), we've made the final decision and we don't need to backtrack to other options for "no odd divisor" (corresponding to, Do no more predicate checking. in your statement #5).
This will yield the following behavior:
| ?- prime(2).
yes
| ?- prime(3).
(1 ms) yes
| ?- prime(6).
(1 ms) no
| ?- prime(7).
yes
| ?-
Note that I did define the prime(2) clause second above. In this case, prime(2) will first fail prime(X) with X = 2, then succeed prime(2) with nowhere else to backtrack. If I had defined prime(2) first, as your first statement (If number is 2, return true and do not check any more predicates.) indicates:
prime(2). % 2 is prime
prime(X) :- % X is prime if...
oddp(X), % X is odd, AND
no_odd_divisors(X). % X has no odd divisors
Then you'd see:
| ?- prime(2).
true ? a
no
| ?-
This would be perfectly valid since Prolog first succeeded on prime(2), then knew there was another clause to backtrack to in an effort to find other ways to make prime(2) succeed. It then fails on that second attempt and returns "no". That "no" sometimes confuses Prolog newcomers. You could also prevent the backtrack on the prime(2) case, regardless of clause order, by defining the clause as:
prime(2) :- !.
Which method you choose depends ultimately on the purpose of your predicate relations. The danger in using cuts is that you might unintentionally prevent alternate solutions you may actually want. So it should be used very thoughtfully and not as a quick patch to reduce outputs.
There are several issues on your program:
Writing a cut, !/0, after a call to false/0 is useless and as the cut will never be reached. Try exchanging the order of these two calls.
The first clause can be simplified to oddp(N) :- N mod 2 =:= 1. You can also apply this simplification in other clauses.
The predicate not/1 is better considered deprecated. Write instead evenp(N) :- \+ oddp(N).. The (\+)/1 is the standard operator/control construct for negation as failure.

prolog unification resolution

Why does this work:
power(_,0,1) :- !.
power(X,Y,Z) :-
Y1 is Y - 1,
power(X,Y1,Z1),
Z is X * Z1.
And this gives a stack overflow exception?
power(_,0,1) :- !.
power(X,Y,Z) :-
power(X,Y - 1,Z1),
Z is X * Z1.
Because arithmetic operations are only performed on clauses through the is operator. In your first example, Y1 is bound to the result of calculating Y - 1. In the later, the system attempts to prove the clause power(X, Y - 1, Z1), which unifies with power(X', Y', Z') binding X' = X, Y' = Y - 1, Z' = Z. This then recurses again, so Y'' = Y - 1 - 1, etc for infinity, never actually performing the calculation.
Prolog is primarily just unification of terms - calculation, in the "common" sense, has to be asked for explicitly.
Both definitions do not work properly.
Consider
?- pow(1, 1, 2).
which loops for both definitions because the second clause can be applied regardless of the second argument. The cut in the first clause cannot undo this. The second clause needs a goal Y > 0 before the recursive goal. Using (is)/2 is still a good idea to get actual solutions.
The best (for beginners) is to start with successor-arithmetics or clpfd and to avoid prolog-cut altogether.
See e.g.: Prolog predicate - infinite loop

Resources