Related
I am doing some toy tests with the CLPFD library in swi-prolog.
Does anybody know why the program below does not work?
start(X,Y):-
Vars = [X,Y],
Vars ins 1..3,
abs(X-Y) #>= 2,
X #>= Y,
nl,
write([X,Y]), nl.
The expected answer for start(X,Y) would be X=3 and Y=1. However, swi-prolog indicates me multiple answers. The program works properly if I replace
abs(X-Y) #>= 2
by
X-Y #>= 2
My question is whether I am using the abs operator in the right way.
First of all, constraints and side-effects do not flock together. Instead, simply stick to the pure part of your program:
start(X,Y):-
Vars = [X,Y],
Vars ins 1..3,
abs(X-Y) #>= 2,
X #>= Y.
And now, query your relation:
?- start(X,Y).
X in 1..3, X#>=Y, abs(X-Y)#>=2, Y in 1..3.
The answer is a conditional one:
Yes, there are solutions for X and Y provided all these conditions hold.
To get actual values, you have to eliminate all these conditions. You have several options:
In this case, you can use labeling/2:
?- start(X,Y), labeling([], [X,Y]).
X = 3, Y = 1.
So there is exactly one solution. The clpfd-solver alone was not powerful enough to come to this conclusion, it needed some extra help.
Even better would be to use contracting/1:
?- start(X,Y), clpfd:contracting([X,Y]).
X = 3, Y = 1.
In contrast to labeling, contracting tries to reduce the size of the domain without (visible) search. This makes the solver a bit stronger.
Reasons why the solver is not powerful enough
in the very general case solving such arithmetic problems is undecidable.
in more specific cases the algorithms would be extremely costly. In fact, there is more than one diophant in the room.
even simpler algorithms are very costly in terms of both implementation effort and runtime.
for many situations, the solver boils down to maintaining consistencies within one constraint1. So the only way to "communicate" between different constraints are the domains of variables.
In your case, the abs-constraint admits more solutions!
?- [X,Y]ins 1..3, abs(X-Y)#>=2, labeling([],[X,Y]).
X = 1, Y = 3
; X = 3, Y = 1.
?- [X,Y]ins 1..3, X-Y#>=2, labeling([],[X,Y]).
X = 3, Y = 1.
What you expect is that the extra constraint X #>= Y would help. Alas, the concrete consistency mechanisms are too weak. And not even X #> Y helps:
?- [X,Y]ins 1..3, abs(X-Y)#>=2, X#>Y.
X in 2..3, Y#=<X+ -1, abs(X-Y)#>=2, Y in 1..2.
However, if you switch from SWI to SICStus, things are a bit different:
| ?- assert(clpfd:full_answer).
| ?- X in 1..3, Y in 1..3, abs(X-Y)#>=2.
Y+_A#=X, X in 1..3, Y in 1..3, _A in{-2}\/{2}.
| ?- X in 1..3, Y in 1..3, abs(X-Y)#>=2, X#>Y.
X = 3, Y = 1.
Please note how abs is resolved!
And using SICStus with library(clpz) has the same strength:
| ?- X in 1..3, Y in 1..3, abs(X-Y)#>=2, X#>Y.
X = 3, Y = 1.
1 Note that I avoid to use the notion of local consistency as opposed to global consistency, since quite often also global consistency only refers to consistency within one "global" constraint.
I am trying to solve first linear programming problem example on http://www.zweigmedia.com/RealWorld/tutorialsf4/framesLinProGr.html. X and Y are zero or positives, their sum can be upto 50, 2X+Y can be upto 60. The function X+3Y has to be maximized.
I am using following code:
mysol2(X,Y,Z):-
X in 0..sup, % Error: Syntax error: Operator expected
Y in 0..sup,
X + Y =< 50,
2 * X + Y =< 60,
Z is max(X + 3*Y).
However, it does not even load (error is indicated above).
With following code:
mysol2(X,Y,Z):-
X >= 0,
Y >= 0,
X + Y =< 50,
2 * X + Y =< 60,
Z is max(X + 3*Y).
The program loads, but on running:
ERROR: >=/2: Arguments are not sufficiently instantiated
How can I correct these errors?
(>=)/2 and (is)/2 are very low-level predicates. You can only use them in very special circumstances. In most cases, these predicates will lead to instantiation errors because one or both arguments are not sufficiently instantiated.
Constraints are a declarative solution in such cases, working correctly in all cases.
For example, you can use CLP(Q) as available in SICStus Prolog with minimal modifications of your code:
:- use_module(library(clpq)).
solution(X, Y) :-
{ X >= 0,
Y >= 0,
X + Y =< 50,
2*X + Y =< 60 }.
Sample query and result:
| ?- solutionX, Y), maximize(X+3*Y).
X = 0,
Y = 50 ? ;
no
The most widely used Prolog and Prolog-like systems (SICStus, ECLiPSe etc.) all ship with powerful constraint libraries, which are meant to be used notably when reasoning over integers and rationals.
I want to write the equivalent psudo-function in prolog:
function underhundred(X){
if (X >= 0 && X <=100) return 1;
else return 0;
}
I tried writing this but it does not compile:
underhundred(X,L) :- L is (X => 0, X =< 100 -> L = 1; L = 0) .
What would be the proper way of writing this without using prolog between predicate?
If you indeed want to use the goals L=1 and L=0 and X is an integer, use clpfd!
:- use_module(library(clpfd)).
Reification works like a charm!
?- X in 0..100 #<==> L.
L in 0..1, X in 0..100#<==>L.
What if X gets instantiated?
?- X in 0..100 #<==> L, X=(-7).
L = 0, X = -7. % out of bounds: -7 < 0 =< 100
?- X in 0..100 #<==> L, X=55.
L = 1, X = 55. % within bounds: 0 =< 55 =< 100
?- X in 0..100 #<==> L, X=111.
L = 0, X = 111. % out of bounds: 0 =< 100 < 111
A Prolog query succeeds or fails. If it succeeds it will return the bindings it made to be true.
You can write this predicate using clpfd as:
:-use_module(library(clpfd)).
under_hundred_clpfd(X):-
X in 0..100.
(You might prefer a name such as between_0_100?, if you literally want under 100 then you can use X in inf..99).
Some queries:
?-under_hundred_clpfd(5).
true.
?-under_hundred_clpfd(101).
false.
?-under_hundred_clpfd(X).
X in 0..100.
A traditional way to write this is:
under_hundred(X):-
X>=0,
X=<100.
But this way does not work for uninstantiated variables.
?-under_hundred(X).
ERROR: >/2: Arguments are not sufficiently instantiated
So like you say you might have to put a between/3 or length/2 goal to get a solution or similar construct.
underhundred(X):-
length(_,X),
X>=0,
X=<100.
This is not a very good solution as on back tracking it will get into an infinite loop. between/3 behaves better but you don't want it :).
If the main point of the question is how to write an if-then-else construct in Prolog, then a reasonable answer along the lines of the proposed definition is:
underhundred(X,L) :-
( X >= 0, X =< 100 )
-> L = 1
; L = 0.
This will only be useful if underhundred(X,L) is called after X has been sufficiently instantiated.
I want to know what #< means in Prolog?
I encountered this symbol in this line of code while reading about the Bridge and Torch Problem:
select_one_or_two(L, [Sel1,Sel2], L2) :-
select(Sel1, L, NewL),
select(Sel2, NewL, L2),
Sel1 #< Sel2.
The comparative operators that start with # are more general than the ones that don't. With operators such as </2, you can only compare numeric values and expressions (involving literal numerics and variables that are instantiated with numeric values). So, with </2 you can do this:
?- X = 2, Y = 3, X + Y < 2*Y.
X = 2,
Y = 3.
?- X = 2, Y = 3, X + Y > 2*Y.
false.
?-
But you will get an error in the following cases if the expressions don't evaluate to a known numeric:
?- Y = 3, X + Y < 2*Y.
ERROR: </2: Arguments are not sufficiently instantiated
Or:
?- a < b.
ERROR: </2: Arithmetic: `a/0' is not a function
However, using #</2 you can compare lots of different types of objects in prolog. The comparison evaluation follows the rules described in the link that #Ankur gave. To understand these rules, you'll need to know what Prolog terminology means, such as term, functor, atom, etc (see, for example, Prolog Terms)
Looking at some examples:
?- a #< b.
true.
?- a(1) #< a(2).
true.
?- b(1) #< a(2).
false.
?- 20 #< a.
true.
These are pretty straight-forward, following the rules. Here's a more interesting case (from above):
?- Y = 3, X + Y #< 2*Y.
false.
Why would X + Y be considered "not less than" 2*Y? Prolog would internally look at this as:
`+(X,3) #< *(2,3).`
(Note Y is instantiated to 3.) These are compound terms (they aren't individual atoms or variables). If we look through the comparison rules, the matching rule is:
Compound terms are first checked on their arity, then on their functor
name (alphabetically) and finally recursively on their arguments,
leftmost argument first.
The arity of both terms is 2. The functor names are + and * respectively. Those are different. And in teh ASCII collating sequence, + comes after *. Therefore it is not true that + "is less than" *, and therefore not true that +(X,3) #< *(2,3). Thus, it is not true that Y = 3, X + Y #< 2 * Y.
Note also that #</2 doesn't evaluate numeric expressions. So even with X and Y instantiated as values, you will get:
?- X = 2, Y = 3, X + Y #< 2*Y.
false.
Whereas, when we had </2 here, this is true, since the expression X + Y < 2*Y, when evaluated, is true. When variables are simply unified, it understands that, however, so you would have:
| ?- X #< Y.
yes
But on the other hand:
| ?- X = 2, Y = 1, X #< Y.
no
In this case X #< Y is seen as 2 #< 1 due to the unification of X with 2 and Y with 1 and the numeric rule kicks in.
Having said all that, the use of #</2 in the predicate select_one_or_two enables that predicate to be usable on lists of all sorts of objects, not just numbers or fully instantiated numeric expressions. If it had used </2, then the following would work:
?- select_one_or_two([2,1,3], X, Y).
X = [2, 3],
Y = [1] ;
X = [1, 2],
Y = [3] ;
X = [1, 3],
Y = [2] ;
false.
But the following fails:
?- select_one_or_two([b,a,c], X, Y).
ERROR: </2: Arithmetic: `b/0' is not a function
?-
However, with the #< operator, it works:
?- select_one_or_two([b,a,c], X, Y).
X = [b, c],
Y = [a] ;
X = [a, b],
Y = [c] ;
X = [a, c],
Y = [b] ;
false.
I have this code that uses an upper bound variable N that is supposed to terminate for X and Y of the pythagorean triple. However it only freezes when it reaches the upper bound. Wasn't sure how to use the cut to stop the backtracking. Code is:
is_int(0).
is_int(X) :- is_int(Y), X is Y+1.
minus(S,S,0).
minus(S,D1,D2) :- S>0, S1 is S-1, minus(S1,D1,D3), D2 is D3+1.
pythag(X,Y,Z,N) :- int_triple(X,Y,Z,N), Z*Z =:= X*X + Y*Y.
int_triple(X,Y,Z,N) :- is_int(S), minus(S,X,S1), X>0, X<N,
minus(S1,Y,Z), Y>0, Y<N.
Will be called, for example with,
?- pythag(X,Y,Z,20).
First, let us test your solution:
?- pythag(X,Y,Z,20).
X = 4, Y = 3, Z = 5
; X = 3, Y = 4, Z = 5
; X = 8, Y = 6, Z = 10
; X = 6, Y = 8, Z = 10
; X = 12, Y = 5, Z = 13
; X = 5, Y = 12, Z = 13
; X = 12, Y = 9, Z = 15
; X = 9, Y = 12, Z = 15
; X = 15, Y = 8, Z = 17
; X = 8, Y = 15, Z = 17
; X = 16, Y = 12, Z = 20
; X = 12, Y = 16, Z = 20
; loops.
Looks perfect to me! All answers are correct solutions! ... up to and including this last solution. After that, your program loops.
Before we try to identify the problem, just hold on for a moment: You must be pretty patient to go through 12 (that is: twelve) answers only to find that loop. Do you think that this method will also work for bigger cases? How many answers are you willing to look at before you give up? Isn't there a simpler way to find out about the problem?
There is one interesting observation here: The answers found have (almost) nothing to do with the looping of the program! That is: By looking at the answers, you get (frequently – as in this case) no clue about the actual cause of the loop! So why not turn off all the answers and concentrate on the relevant part! In fact, we can do this as follows:
?- pythag(X,Y,Z,20), false.
loops.
Now, all answers have been removed due to the goal false. What remains is just the final outcome: either termination, or non-termination, or some error. Nothing else. This should facilitate our observations about termination a bit - no more blinding answers scrolling over the screen. Note that this does not solve the problem in general. After all, how long are we willing to wait? 1s ? 1m?
The actual reason of non-termination can be best understood by looking at a relevant failure slice. That is a fragment of the program whose non-termination implies the non-termination of the whole program. See this answer for more details. Here is the relevant failure slice of your program for query pythag(X,Y,Z,20), false:
pythag(X,Y,Z,N) :-
int_triple(X,Y,Z,N), false,
Z*Z =:= X*X + Y*Y.
int_triple(X,Y,Z,N) :-
is_int(S), false,
minus(S,X,S1), X>0, X<N,
minus(S1,Y,Z), Y>0, Y<N.
is_int(0) :- false.
is_int(X) :-
is_int(Y), false,
X is Y+1.
Note that there are not many things left of your program. E.g., the actual equation is gone (that's more or less the logic part...). Still, this fragment is relevant. And as long as you do not change something within that fragment, the problem will persist! That is guaranteed for a pure monotonic program as this one...
Here is my preferred solution: It uses length/2 and between/3, two frequently supported predicates of the Prolog prologue.
pythag2(X,Y,Z,N) :-
length(_, N),
between(1,N,X),
between(1,N,Y),
between(1,N,Z),
Z*Z =:= X*X + Y*Y.
I was recently as well thinking about a Prolog solution to
find Pythagorean triples. I came up with a slightly different
code. Assume we have a function:
isqrt(a) = floor(sqrt(a))
It is then enough to enumerate x and y, and to check whether
x*x+y*y is the square of some z. Namely to check for:
h = x*x+y*y, z = isqrt(h), z*z = h ?
The function isqrt can be implemented via bisection. For
symmetry breaking we can enumerate y after x. Assuming
N = 99 the resulting code is:
% between(+Integer, +Integer, -Integer)
between(Lo, Hi, _) :-
Lo > Hi, !, fail.
between(Lo, _, Lo).
between(Lo, Hi, X) :-
Lo2 is Lo+1, between(Lo2, Hi, X).
% bisect(+Integer, +Integer, +Integer, -Integer)
bisect(Lo, Hi, X, Y) :-
Lo+1 < Hi, !,
M is (Lo+Hi) // 2,
S is M*M,
(S > X -> bisect(Lo, M, X, Y);
S < X -> bisect(M, Hi, X, Y);
M = Y).
bisect(Lo, _, _, Lo).
% pythago(-List)
pythago(X) :-
X = [A,B,C],
between(1, 99, A),
between(A, 99, B),
H is A*A+B*B,
bisect(0, H, H, C),
C =< 99, H =:= C*C.
There should be 50 such Pythagorean tripples, see also Sloan's A046083:
?- findall(-, pythago(_), L), length(L, N).
N = 52.
One might like to cross check with the following
CLP(FD) solution.
:- use_module(library(clpfd)).
% pythago3(-List)
pythago3(X) :-
X = [A,B,C],
X ins 1..99,
A*A+B*B #= C*C,
A #=< B,
label(X).
It gives the same number of solutions:
?- findall(-, pythago3(_), L), length(L, N).
N = 50.