Get Prolog to give all possibilities for arithmetic - prolog

I was wondering whether in prolog it is possible to get it to brute force all the possible calculations for something like this:
6 is Z + Q
Z = 1 Q = 5
Z = 2 Q = 4
Z = 3 Q = 3

I suggest to use, if your Prolog support it, a Finite Domain solver.
I usually use GProlog and I can obtain what you ask with something like
fd_domain([A, B], 1, 100),
6 #= A + B,
fd_labeling([A, B]),
where fd_domain/3 set the domain for variables A and B (from 1 to 100), 6 #= A + B set the constraint (A + B is 6) and fd_labelling/1 get all possibles calculations.
In Swi-Prolog is a little different.
First of all, you have to load the CLP(FD) library with
:- use_module(library(clpfd)).
To set the variables and the domain, you can write
Vars = [A, B],
Vars ins 1..100,
Setting the constraint is equal
6 #= A + B,
and to get all possible combinations, you can write
label(Vars),

The generate-and-test approach also works. Of course, you still need some constraints, for example:
?- between(1, 6, X), % X is an integer between 1 and 6
between(1, 6, Y), % Y is an integer between 1 and 6
X =< Y, % X is not larger than Y
X + Y =:= 6. % the sum is 6
X = 1, Y = 5 ;
X = 2, Y = 4 ;
X = Y, Y = 3 ;
false.
The order of the subqueries is significant, so you could as well call it generate-then-test. If you are not afraid to hard-code some of the constraints, there might be ways to avoid generating some of the values, and make some of the tests unnecessary, for example:
?- between(1, 6, X), % X is an integer between 1 and 6
between(X, 6, Y), % Y is an integer between X and 6
X + Y =:= 6. % the sum is 6
X = 1, Y = 5 ;
X = 2, Y = 4 ;
X = Y, Y = 3 ;
false.
You should realize that going down that road far enough is about the same as implementing a constraint solver like CLP(FD) for example.

Related

How do I fix this triangular sequence (recursion) in prolog Arguments are not sufficiently instantiated?

Trying to calculate the triangular number sequence in Prolog.
This is my solution:
where X is the nth position of the sequence and Y is the result.
triang(1, 1).
triang(X, Y) :-
X>0,
A is X - 1,
triang(A, B),
Y is B + X.
?- triang(5,X).
X = 15
But when i try to do for example triang(X,10) I receive an error
Arguments are not sufficiently instantiated.
I guess this is because X is not defined in the consult.
is there any recommendation how to solve this problem,thank you.
First of all, the result you got is not that bad. It says: sorry, I am unable to come to a conclusion and before producing an incorrect result, I prefer to produce an error.
The actual reason is the following goal
?- X > 0.
error(instantiation_error,(is)/2).
So here we ask for X that are greater than zero. And there are many, in fact infinitely many. There is no direct way to enumerate that set for this built-in and thus it prefers the error.
However, with library(clpz) or clpfd, there is a better way:
:- use_module(library(clpz)). % use clpfd for SWI instead
:- op(150, fx, #).
triang(0, 0).
triang(X, Y) :-
#X #>0,
#Y #>0,
#A #= #X - 1,
#Y #= #B + #X,
triang(A, B).
?- triang(X,15).
X = 5
; false.
?- triang(X,14).
false.
?- triang(X,X).
X = 0
; X = 1
; false.
?- triang(X,Y).
X = 0, Y = 0
; X = 1, Y = 1
; X = 2, Y = 3
; X = 3, Y = 6
; X = 4, Y = 10
; X = 5, Y = 15
; X = 6, Y = 21
; ... .
?- #X #> 0.
clpz:(X in 1..sup).
So now there is an answer to #X #> 0. The answer is often called a constraint. In this case it tells us that X must be in the interval 1 up to (kind of) infinity.

Prolog: 'cut' in query vs. rules/facts

Doing exercise 10.4 on learnprolognow, could someone explain to me or help me visualize why for ?- p(X),p(Y) we get:
X=1,Y=1; X=1,Y=2; X=2, Y=1; X=2, Y=1.
And not just
X=1, Y=1; X=1, Y=2.
I think I'm misunderstanding how the cut happens, when it's in the ruleset instead of the query - because I think I can visualize it for ?- p(X),!,p(Y)., where it actually behaves as I thought the last one would...
Edit: From the website
% database
p(1).
p(2):-!.
p(3).
% Queries
p(X). % returns: X=1; X=2.
p(X),p(Y). % returns: X=1,Y=1; X=1, Y=1; X=2, Y=2. (?)
p(X),!,p(Y). % returns X=1, Y=1; X=1, Y=2.
To understand this problem you can imagine a tree with X in as first level and Y as second level (prolog uses sld resolution that can be well described with a tree). Consider this problem:
p(1).
p(2):-!.
p(3).
sol(X,Y):-
p(X),
p(Y).
I've added the predicate solve/2 to make it more clear. Run the query:
?- solve(X,Y).
First of all you have to choose the value for X. Prolog uses depth first search from the top to the bottom, from left to right. So it evaluates p(x): p(1) succeed (because is the first clause, if you write p(2) above p(1), p(2) will succeed) and so X = 1. Then evaluates p(Y): p(1) succeed and so you have the first solution:
X = Y, Y = 1.
If you click on more, then prolog does a backtrack (you can imagine this as a step above on the tree) and tries another value for p(Y). In this case p(2) succeed, the predicate is true and you get:
X = 1, Y = 2.
Now, if you click on more, due to the fact there is a cut (!) in the body of p(2) (a general rule in prolog has the form head :- body), prolog will not go more in depth and p(3) is ignored. So there's no more solution to p(Y). So there is another backtracking and this time, for p(X), p(2) succeed and X = 2 and for p(Y), p(1) succeed and you get:
X = 2, Y = 1.
If you click on more, you get:
X = Y, Y = 2.
Now, due to the fact there is a cut after p(2), there are no more solutions available for both X and Y (! cuts everything below p(2)).
If you remove the cut you get all the possible solutions:
X = Y, Y = 1
X = 1,
Y = 2
X = 1,
Y = 3
X = 2,
Y = 1
X = Y, Y = 2
X = 2,
Y = 3
X = 3,
Y = 1
X = 3,
Y = 2
X = Y, Y = 3
Keep in mind that the order of the clauses is important. If you write
p(2).
p(1):-!.
p(3).
You get
X = Y, Y = 2
X = 2,
Y = 1
X = 1,
Y = 2
X = Y, Y = 1
You can check this behaviour using the tracer. In SWI or SWISH you can write ?-
trace, solve(X,Y).
If you have a situation like this:
p(1).
p(2).
p(3).
sol(X,Y):-
p(X),
!,
p(Y).
prolog will tests all the possible values for Y and only one value for X because the cut stops the exploration of the tree (ideally you have 3 branches for X (1,2,3) and 3 for Y (1,2,3), ! cuts 2 and 3 from X) and you get:
X = Y, Y = 1
X = 1,
Y = 2
X = 1,
Y = 3
Sorry for the long post, hope to be clear.

Enumerating domains in Prolog's clpfd

I'm exploring dependent structures of constraints like this one:
assign(X,Y) :-
X in 1..5,
((X mod 2 #= 1) #=> Y in 2..3),
((X mod 2 #= 0) #=> Y #= 5).
What I'm looking for is a representation of X's and Y's domains that is as sparse as possible - in this case it would be something along the lines of X in {1,3,5} and Y in {2,3} or X in {2,4} and Y = 5.
One way of doing that would be to detect all variables on the left side of the #=>, enumerate all their values and collect and merge them together, something like ?- assign(X, Y), findall(X-D, (indomain(X),fd_dom(Y,D)), C), do stuff with C, but maybe there is a more efficient way?
I've also encountered an error trying to label([X,Y]): Arguments are not sufficiently instantiated that goes away when I add another constraint on Y's domain.
When should I expect this error to occur? I feel I have a poor understanding of clpfd's mechanisms and limitations, are there any resources I could learn from? I already know the basics of constraint programming, arc consistency etc.
To keep clpfd enumeration predicates (like indomain/1, label/1, labeling/2, etc.) from ever throwing instantiation errors, simply ensure that all variables have been assigned some finite domain before any enumeration predicates is executed.
So how about directly translating what you wrote to code?
assign(X,Y) :- X in 1\/3\/5, Y in 2..3. % X in {1,3,5} and Y in {2,3}
assign(X,Y) :- X in 2..4, Y in 5. % or X in {2,4} and Y = 5
A simple query (with SWI-Prolog):
?- assign(X,Y), labeling([],[X,Y]).
X = 1, Y = 2
; X = 1, Y = 3
; X = 3, Y = 2
; X = 3, Y = 3
; X = 5, Y = 2
; X = 5, Y = 3
; X = 2, Y = 5
; X = 3, Y = 5
; X = 4, Y = 5.

How to add domain variable to global_cardinality?

I'm trying to add a constraint global_cardinality to my program and in the manual of SICStus Prolog is written:
global_cardinality(+Xs,+Vals)
global_cardinality(+Xs,+Vals,+Options)
where Xs = [X1,...,Xd] is a list of integers or domain variables, and
Vals = [K1-V1,...,Kn-Vn] is a list of pairs where each key Ki is a
unique integer and Vi is a domain variable or an integer. True if
every element of Xs is equal to some key and for each pair Ki-Vi,
exactly Vi elements of Xs are equal to Ki.
Now I can write:
global_cardinality([A,B,C], [1-2, 2-1]).
to say that the number 1 will be used twice. The number 2 will be used just once.
But I would like to say that the number 1 will be used: once, twice or three times
According to the manual I need a domain variable but what is the proper syntax for that?
?- X in 1..3, global_cardinality([A,B,C], [1-X, 2-1]).
not sure about this, but from SWI-Prolog page I think you could try
...global_cardinality([A,B,C], [1-X, 2-1]), (X #= 1 #\/ X #= 2 #\/ X #= 2)...
or
?- global_cardinality([A,B,C], [1-X, 2-1]), X in 1..3, label([A,B,C]).
A = B, B = 1,
C = X, X = 2 ;
A = C, C = 1,
B = X, X = 2 ;
A = X, X = 2,
B = C, C = 1.

Understanding prolog [lists]

I am to write a program that does this:
?- pLeap(2,5,X,Y).
X = 2,
Y = 3 ;
X = 3,
Y = 4 ;
X = 4,
Y = 5 ;
X = 5,
Y = 5 ;
false.
(gives all pairs X,X+1 between 2 and 5, plus the special case at the end).
This is supposedly the solution. I don't really understand how it works, could anyone guide me through it?
pLeap(X,X,X,X).
pLeap(L,H,X,Y) :-
L<H,
X is L,
Y is X+1.
pLeap(L,H,X,Y) :-
L=<H,
L1 is L+1,
pLeap(L1,H,X,Y).
I'd do it simply like this:
pLeap(L,H,X,Y) :-
X >= L,
X =< H,
Y is X+1.
Why doesn't it work (ignoring the special case at the end)?
You could use library clpfd for you problem.
:- use_module(library(clpfd)).
pLeap(L,H,X,Y) :-
X in L..H,
Y #= min(H, X+1),
label([X]).
Here is the output:
?- pLeap(2,5,X,Y).
X = 2,
Y = 3 ;
X = 3,
Y = 4 ;
X = 4,
Y = 5 ;
X = 5,
Y = 5.
The >= and =< operators don't instantiate their arguments, and you can only use them once the arguments have already been instantiated.
Put another way, in the given solution, X and Y are given values with is, and the < and =< operators are only used on L and H, whose values are given by the user. (On the given solution, try pLeap(L,H,2,3) and you'll get the same problem as you're having.)
In your case, though, you try to use >= and =< on X, which has no value yet, and so the interpreter complains.

Resources