Constraint - SWI- Prolog Queries - prolog

I have an exam coming up and I'm going through past papers to help my understanding.
I came across the following past paper question:
Consider the following queries and answers. Some answers coincide with
what SWI-Prolog would infer whereas others are erroneous. Indicate which
answers are genuine and which ones are fake (no explanation of your answer
is required).
(i) |?- [A, B, C] ins 0 .. 2, A #= B + C.
A = 0..2 B = 0..2 C = 0..2
(ii) |?- A in 0 .. 3, A * A #= A.
A = 0..2
(iii) |?- [A, B] ins -1 .. 1, A #= B.
A = 1 B = 1
(iv) |?- [A, B] ins 0 .. 3, A #= B + 1.
A = 1..3 B = 1..2
I'm struggling to see how each one is either true or false. Would someone be able to explain to me how to figure these out please.
Thank you, really appreciate the help.

The key principle for deciding which answers are admissible and which are not is to look whether the residual program is declaratively equivalent to the original query. If the residual constraints admit any solution that the original query does not, or the other way around, then the answer is fake (or you have found a mistake in the CLP(FD) solver). If the shown answer is not even syntactically valid, then the answer is definitely fake.
Let's do it:
(i) |?- [A, B, C] ins 0 .. 2, A #= B + C.
suggested answer: A = 0..2 B = 0..2 C = 0..2
WRONG! The original query constrains the variables to integers, but this answer is not even a syntactically valid Prolog program.
(ii) |?- A in 0 .. 3, A * A #= A.
suggested answer: A = 0..2
WRONG! The original query constrains A to integers, but according to this residual program, A = 0..2 is a valid solution. The term ..(0, 2) is not an integer.
(iii) |?- [A, B] ins -1 .. 1, A #= B.
suggested answer: A = 1 B = 1
WRONG! Not syntactically valid.
(iv) |?- [A, B] ins 0 .. 3, A #= B + 1.
suggested answer: A = 1..3 B = 1..2
WRONG! Not syntactically valid.
Note that even if all shown answers were syntactically valid and =/2 were replaced by in/2 in the residual goals of (i), (ii) and (iv), these answer would still be all wrong, because you can in each case find solutions that either are not admissible by the original query or the residual goals, but not both. I leave solving these cases as an exercise for you, for example, suppose the respective answers are:
A in 0..2, B in 0..2, C in 0..2.
A in 0..2.
A = 1, B = 1.
A in 1..3, B in 1..2.
and find a witness for each case to show that the residual goals are semantically different from the respective original query.
For example, in case (1), A = B = C = 2 would be a valid solution according to the residual constraints, but obviously the original constraints exclude this solution, because 2 #= 2 + 2 does not hold!

A variable is always restricted to get a value contained in its domain, and arithmetic constrains only reduce the domain of involved variables.
So, try to 'label' all variables - that is, assign values from answers reported domains. Of course, if the arithmetic relation is not satisfied, you can say the answer is faked. Take ii). Does it hold for A=0 ? What about A=2 ?
This 'test' of course doesn't suffice to answer all questions. Some reported domains are narrower. For instance, take iii). Can you see any reason that excludes -1, or 0. If you cannot, you should mark the answer as faked.

Related

Difference between `is` and `=`?

I have defined these two plus predicates:
plus1(A, B, C):- C is A + B.
plus2(A, B, C):- C = A + B.
As expected, plus1(4, 5, X) gives the result X = 9.
But plus2(4, 5, X) gives the result X = 4+5, which is not wrong, but I had expected the result 9.
So why does it not perform any calculation, what is the difference between is and =, and when should I use what?
I didn't want to leave this question without an answer.
As Willem Van Onsem has commented, is is for evaluates the numerical expressions while = is for unification.
I also found this article about unification for future readers with the same question.

Why doesn't this clpfd query terminate until I add a redundant constraint?

I've written some predicates which take the length of a list and attaches some constraints to it (is this the right vocabulary to be using?):
clp_length([], 0).
clp_length([_Head|Rest], Length) :-
Length #>= 0, Length #= Length1 + 1,
clp_length(Rest, Length1).
clp_length2([], 0).
clp_length2([_Head|Rest], Length) :-
Length #= Length1 + 1,
clp_length2(Rest, Length1).
The first terminates on this simple query, but the second doesn't:
?- Small in 1..2, clp_length(Little, Small).
Small = 1,
Little = [_1348] ;
Small = 2,
Little = [_1348, _2174] ;
false.
?- Small in 1..2, clp_length2(Little, Small).
Small = 1,
Little = [_1346] ;
Small = 2,
Little = [_1346, _2046] ;
% OOPS %
This is strange to me, because Length is pretty clearly greater than 0. To figure that out you could either search, find the zero, and deduce that adding from zero can only increase the number, or you could propagate the in 1..2 constraint down. It feels like the extra clause is redundant! That it isn't means my mental model of clpfd is pretty wrong.
So I think I have two questions (would appreciate answers to the second as comments)
Specifically, why does this additional constraint cause the query to work correctly?
Generally, is there a resource I can use to learn about how clpfd is implemented, instead of just seeing some examples of how it can be used? I'd prefer not to have to read Markus Triska's thesis but that's the only source I can find. Is that my only option if I want to be able to answer questions like this one?
1mo, there is the issue with naming. Please refer to previous answers by
mat
and me recommending relational names. You won't go far using inappropriate names. So list_length/2 or list_fdlength/2 would be an appropriate name. Thus we have list_fdlength/2 and list_fdlength2/2.
2do, consider the rule of list_fdlength2/2. Nothing suggests that 0 is of relevance to you. So that rule will be exactly the same if you are using 0 or 1 or -1 or whatever as base case. So how should this poor rule ever realize that 0 is the end to you? Better, consider a generalization:
list_fdlength2(fake(N), N) :- % Extension to permit fake lists
N #< 0.
list_fdlength2([], 0).
list_fdlength2([_Head|Rest], Length) :-
Length #= Length1 + 1,
list_fdlength2(Rest, Length1).
This generalization shows all real answers plus fake answers. Note that I have not changed the rule, I added this alternative fact only. Thus the fake solutions are actually caused by the rule:
?- list_fdlength2(L, 1).
L = [_A]
; L = [_A, _B|fake(-1)]
; L = [_A, _B, _C|fake(-2)]
; ... .
?- list_fdlength2(L, 0).
L = []
; L = [_A|fake(-1)]
; L = [_A, _B|fake(-2)]
; ... .
Each clause tries to contribute to the solutions just in the scope of the clause. But there is no way to derive (by the built-in Prolog execution mechanism) that some rules are no longer of relevance. You have to state that explicitly with redundant constraints as you did.
Now, back to your original solution containing the redundant constraint Length #>= 0. There should not be any such fake solution at all.
list_fdlength(fake(N), N) :-
N #< 0.
list_fdlength([], 0).
list_fdlength([_Head|Rest], Length) :-
Length #>= 0,
Length #= Length1 + 1,
list_fdlength(Rest, Length1).
?- list_fdlength(L, 1).
L = [_A]
; L = [_A, _B|fake(-1)] % totally unexpected
; false.
?- list_fdlength(L, 0).
L = []
; L = [_A|fake(-1)] % eek
; false.
There are fake answers, too! How ugly! At least, they are finite in number. But, you could have done it better by using
Length #>= 1 in place of Length #>=0. With this little change, there are no longer any fake solutions when N is non-negative and thus also your original program will be better.

Prolog program to get an (integer) number as the sum of two integer squares, why does it not work?

I'm starting learning Prolog and I want a program that given a integer P gives to integers A and B such that P = A² + B². If there aren't values of A and B that satisfy this equation, false should be returned
For example: if P = 5, it should give A = 1 and B = 2 (or A = 2 and B = 1) because 1² + 2² = 5.
I was thinking this should work:
giveSum(P, A, B) :- integer(A), integer(B), integer(P), P is A*A + B*B.
with the query:
giveSum(5, A, B).
However, it does not. What should I do? I'm very new to Prolog so I'm still making lot of mistakes.
Thanks in advance!
integer/1 is a non-monotonic predicate. It is not a relation that allows the reasoning you expect to apply in this case. To exemplify this:
?- integer(I).
false.
No integer exists, yes? Colour me surprised, to say the least!
Instead of such non-relational constructs, use your Prolog system's CLP(FD) constraints to reason about integers.
For example:
?- 5 #= A*A + B*B.
A in -2..-1\/1..2,
A^2#=_G1025,
_G1025 in 1..4,
_G1025+_G1052#=5,
_G1052 in 1..4,
B^2#=_G406,
B in -2..-1\/1..2
And for concrete solutions:
?- 5 #= A*A + B*B, label([A,B]).
A = -2,
B = -1 ;
A = -2,
B = 1 ;
A = -1,
B = -2 ;
etc.
CLP(FD) constraints are completely pure relations that can be used in the way you expect. See clpfd for more information.
Other things I noticed:
use_underscores_for_readability_as_is_the_convention_in_prolog instead ofMixingTheCasesToMakePredicatesHardToRead.
use declarative names, avoid imperatives. For example, why call it give_sum? This predicate also makes perfect sense if the sum is already given. So, what about sum_of_squares/3, for example?
For efficiency sake, Prolog implementers have choosen - many,many years ago - some compromise. Now, there are chances your Prolog implements advanced integer arithmetic, like CLP(FD) does. If this is the case, mat' answer is perfect. But some Prologs (maybe a naive ISO Prolog compliant processor), could complain about missing label/1, and (#=)/2. So, a traditional Prolog solution: the technique is called generate and test:
giveSum(P, A, B) :-
( integer(P) -> between(1,P,A), between(1,P,B) ; integer(A),integer(B) ),
P is A*A + B*B.
between/3 it's not an ISO builtin, but it's rather easier than (#=)/2 and label/1 to write :)
Anyway, please follow mat' advice and avoid 'imperative' naming. Often a description of the relation is better, because Prolog it's just that: a relational language.

Defining the rule to avoid Arithmetic Error is/2 in Prolog

I'm trying to implement a binary tree in Prolog and I'm getting the following error:
ERROR: is/2: Arithmetic: `z/0' is not a function
I understand that this error occurs because the RHS of is/2 has not been instantiated properly.
But I'm finding it difficult to figure out how to instantiate my variable z in the second tree_eval/3 .
tree_eval(_,tree(empty,Num,empty),Num).
tree_eval(Value,tree(empty,z,empty),Value):-
z = Value.
tree_eval(Value,tree(L,Op,R),Eval):-
tree_eval(Value,L,LEval),
tree_eval(Value,R,REval),
eval(LEval,REval,Op,Eval).
eval(LEval,REval,Op,Result):-
Op = '+',
Result is LEval + REval.
eval(LEval,REval,Op,Result):-
Op = '-',
Result is LEval - REval.
eval(LEval,REval,Op,Result):-
Op ='/',
Result is LEval/REval.
How do I assign Value to z here?
Thanks in advance.
By z=Value you are actually unifying the variable Value with the atom z. If you mean z to be a variable you have to write it as a capital letter Z as #vmg pointed out:
tree_eval(Value,tree(empty,Z,empty),Value):-
Z = Value.
In that case it is interesting to consider what happens when querying the predicate:
?- tree_eval(V,tree(empty,3,empty),E).
E = 3 ? ;
E = V = 3 ? ;
no
The first solution is produced by your first rule in which you have an anonymous variable for the first argument. The second solution is produced by your second rule in which you demand the first and third arguments to be the same. So essentially you have two derivation paths for the value that is 3 in both cases. Now let's look at a slightly bigger tree:
?- tree_eval(V,tree(tree(empty,2,empty),+,tree(empty,3,empty)),E).
E = 5 ? ;
E = 5,
V = 3 ? ;
E = 5,
V = 2 ? ;
no
The first answer is hardly suprising as the tree evaluates to 5 indeed but what about the next two solutions? Let's take a look how prolog comes to these solutions: The term
tree(tree(empty,2,empty),+,tree(empty,3,empty))
matches only with the third rule because the subtrees are not empty. So tree_eval/3 is called with the subtree tree(empty,2,empty) and yields as first solution: (_,tree(empty,2,empty),2)
Then tree_eval/3 is called for the subtree tree(empty,3,empty) with the first solution: (_,tree(empty,3,empty),3)
Now eval(2,3,+,Eval) yields Eval=5. The first argument however is still Value=_. So the first solution for the query is: E=5.
If you ask for other answers prolog will try if there are further solutions for the 3rd goal, determine there are none, hence backtracking to the 2nd goal: the right subtree, and indeed your second rule delivers: (3,tree(empty,3,empty),3). eval(2,3,+,Eval) again contributes Eval=5, hence the second solution to the query: E=5, V=3.
If you still ask for more solutions prolog has to backtrack further to the 1st goal: and your 2nd rule delivers again: (2,tree(empty,2,empty),2). Now your second goal again matches the 1st rule: (_,tree(empty,3,empty),3), eval/4 delivers again Eval=5, so the 3rd solution to the query is: E=5, V=2
Backtracking one more time to goal 2 prolog is trying the 2nd rule again and fails because the first argument can't be 2 and 3 at the same time. So there is no 4th solution to the query.
However, if tree_eval/3 would be queried with the same tree structure but both leaves being 2 it is, by the above reasoning, not really suprising that there are 4 solutions:
?- tree_eval(V,tree(tree(empty,2,empty),+,tree(empty,2,empty)),E).
E = 4 ? ;
E = 4,
V = 2 ? ;
E = 4,
V = 2 ? ;
E = 4,
V = 2 ? ;
no
Looking at those multiple solutions it is apparent that your third argument is delivering the correct solution, and that you don't really need the first argument. You can also do without the second rule. Incorporating some improvements suggested by #mat, your predicate might then look something like that:
tree_evaluation(tree(empty,Num,empty),Num).
tree_evaluation(tree(L,Op,R),Val) :-
tree_evaluation(L,LVal),
tree_evaluation(R,RVal),
evaluation(LVal,RVal,Op,Val).
evaluation(L,R,+,V) :-
V is L + R.
evaluation(L,R,-,V) :-
V is L - R.
evaluation(L,R,/,V) :-
V is L / R.
This version yields unique answers:
?- tree_evaluation(tree(empty,3,empty),E).
E = 3 ? ;
no
?- tree_evaluation(tree(tree(empty,2,empty),+,tree(empty,3,empty)),E).
E = 5 ? ;
no

Prolog: simulate disjunctive facts

I've got a logic problem that I'd like to solve, so I thought, "I know, I'll try Prolog!"
Unfortunately, I'm running into a brick wall almost immediately. One of the assumptions involved is a disjunctive fact; either A, B or C is true (or more than one), but I do not know which. I've since learned that this is something Prolog does not support.
There's a lot of documentation out there that seems to address the subject, but most of it seems to immediately involve more intricate concepts and solves more advanced problems. What I'm looking for is an isolated way to simulate defining the above fact (as defining it straight away is, by limitations of Prolog, not possible).
How could I address this? Can I wrap it in a rule somehow?
EDIT: I realise I have not been very clear. Given my lack of familiarity with Prolog, I did not want to get caught up in a syntax error when trying to convey the problem, and instead went with natural language. I guess that did not work out, so I'll give it a shot in pseudo-Prolog anyway.
Intuitively, what I would want to do would be something like this, to declare that either foo(a), foo(b) or foo(c) holds, but I do not know which:
foo(a); foo(b); foo(c).
Then I would expect the following result:
?- foo(a); foo(b); foo(c).
true
Unfortunately, the fact I'm trying to declare (namely foo(x) holds for at least one x \in {a, b, c}) cannot be defined as such. Specifically, it results in No permission to modify static procedure '(;)/2'.
Side-note: after declaring the disjunctive fact, the result of ?- foo(a). would be a bit unclear to me from a logical perspective; it is clearly not true, but false does not cover it either -- Prolog simply does not have sufficient information to answer that query in this case.
EDIT 2: Here's more context to make it more of a real-world scenario, as I might have over-simplified and lost details in translation.
Say there are three people involved. Alice, Bob and Charlie. Bob holds two cards out of the set {1, 2, 3, 4}. Alice asks him questions, in response to which he shows her one card that Charlie does not see, or shows no cards. In case more cards are applicable, Bob shows just one of them. Charlie's task is to learn what cards Bob is holding. As one might expect, Charlie is an automated system.
Alice asks Bob "Do you have a 1 or a 2?", in response to which Bob shows Alice a card. Charlie now learns that Bob owns a 1 or a 2.
Alice then asks "Do you have a 2 or a 3", to which Bob has no cards to show. Clearly, Bob had a 1, which he showed Alice previously. Charlie should now be able to derive this, based on these two facts.
What I'm trying to model is the knowledge that Bob owns a 1 or a 2 (own(Bob, 1) \/ own(Bob, 2)), and that Bob does not own a 2 or a 3 (not (own(Bob, 2) \/ own(Bob, 3))). Querying if Bob owns a 1 should now be true; Charlie can derive this.
The straight-forward answer to your question:
if you can model your problem with constraint logic programming over finite domains, then, an "exclusive or" can be implemented using #\ as follows:
Of the three variables X, Y, Z, exactly one can be in the domain 1..3.
D = 1..3, X in D #\ Y in D #\ Z in D
To generalize this, you can write:
disj(D, V, V in D #\ Rest, Rest).
vars_domain_disj([V|Vs], D, Disj) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
and use it as:
?- vars_domain_disj([X,Y,Z], 2 \/ 4 \/ 42, D).
D = (Y in 2\/4\/42#\ (Z in 2\/4\/42#\ (X in 2\/4\/42#\D))).
If you don't use CLP(FD), for example you can't find a nice mapping between your problem and integers, you can do something else. Say your variables are in a list List, and any of them, but exactly one, can be foo, and the rest cannot be foo, you can say:
?- select(foo, [A,B,C], Rest), maplist(dif(foo), Rest).
A = foo,
Rest = [B, C],
dif(B, foo),
dif(C, foo) ;
B = foo,
Rest = [A, C],
dif(A, foo),
dif(C, foo) ;
C = foo,
Rest = [A, B],
dif(A, foo),
dif(B, foo) ;
false.
The query reads: in the list [A,B,C], one of the variables can be foo, then the rest must be different from foo. You can see the three possible solutions to that query.
Original answer
It is, sadly, often claimed that Prolog does not support one thing or another; usually, this is not true.
Your question is not exactly clear at the moment, but say you mean that, with this program:
foo(a).
foo(b).
foo(c).
You get the following answer to the query:
?- foo(X).
X = a ;
X = b ;
X = c.
Which you probably interpreted as:
foo(a) is true, and foo(b) is true, and foo(c) is true.
But, if I understand your question, you want a rule which says, for example:
exactly one of foo(a), foo(b), and foo(c) can be true.
However, depending on the context, that it, the rest of your program and your query, the original solution can mean exactly that!
But you really need to be more specific in your question, because the solution will depend on it.
Edit after edited question
Here is a solution to that particular problem using constraint programming over finite domains with the great library(clpfd) by Markus Triska, available in SWI-Prolog.
Here is the full code:
:- use_module(library(clpfd)).
cards(Domain, Holds, QAs) :-
all_distinct(Holds),
Holds ins Domain,
maplist(qa_constraint(Holds), QAs).
qa_constraint(Vs, D-no) :-
maplist(not_in(D), Vs).
qa_constraint([V|Vs], D-yes) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
not_in(D, V) :- #\ V in D.
disj(D, V, V in D #\ Rest, Rest).
And two example queries:
?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 1.
X = 1,
Y = 4 ;
false.
If the set of cards is {1,2,3,4}, and Bob is holding two cards, and when Alice asked "do you have 1 or 2" he said "yes", and when she asked "do you have 2 or 3" he said no, then: can Charlie know if Bob is holding a 1?
To which the answer is:
Yes, and if Bob is holding a 1, the other card is 4; there are no further possible solutions.
Or:
?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 3.
false.
Same as above, can Charlie know if Bob is holding a 3?
Charlie knows for sure that Bob is not holding a three!
What does it all mean?
:- use_module(library(clpfd)).
Makes the library available.
cards(Domain, Holds, QAs) :-
all_distinct(Holds),
Holds ins Domain,
maplist(qa_constraint(Holds), QAs).
This defines the rule we can query from the top level. The first argument must be a valid domain: in your case, it will be 1..4 that states that cards are in the set {1,2,3,4}. The second argument is a list of variables, each representing one of the cards that Bob is holding. The last is a list of "questions" and "answers", each in the format Domain-Answer, so that 1\/2-yes means "To the question, do you hold 1 or 2, the answer is 'yes'".
Then, we say that all cards that Bob holds are distinct, and each of them is one of the set, and then we map each of the question-answer pairs to the cards.
qa_constraint(Vs, D-no) :-
maplist(not_in(D), Vs).
qa_constraint([V|Vs], D-yes) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
The "no" answer is easy: just say that for each of the cards Bob is holding, it is not in the provided domain: #\ V in D.
not_in(D, V) :- #\ V in D.
The "yes" answer means that we need an exclusive or for all cards Bob is holding; 2\/3-yes should result in "Either the first card is 2 or 3, or the second card is 2 or 3, but not both!"
disj(D, V, V in D #\ Rest, Rest).
To understand the last one, try:
?- foldl(disj(2\/3), [A,B], Rest, C in 2\/3 #\ Rest).
Rest = (A in 2\/3#\ (B in 2\/3#\ (C in 2\/3#\Rest))).
A generate-and-test solution in vanilla Prolog:
card(1). card(2). card(3). card(4).
owns(bob, oneof, [1,2]). % i.e., at least one of
owns(bob, not, 2).
owns(bob, not, 3).
hand(bob, Hand) :-
% bob has two distinct cards:
card(X), card(Y), X < Y, Hand = [X, Y],
% if there is a "oneof" constraint, check it:
(owns(bob, oneof, S) -> (member(A,S), member(A, Hand)) ; true),
% check all the "not" constraints:
((owns(bob, not, Card), member(Card,Hand)) -> false; true).
Transcript using the above:
$ swipl
['disjunctions.pl'].
% disjunctions.pl compiled 0.00 sec, 9 clauses
true.
?- hand(bob,Hand).
Hand = [1, 4] ;
;
false.
Note that Prolog is Turing complete, so generally speaking, when someone says "it can't be done in Prolog" they usually mean something like "it involves some extra work".
Just for the sake of it, here is a small program:
card(1). card(2). card(3). card(4). % and so on
holds_some_of([1,2]). % and so on
holds_none_of([2,3]). % and so on
holds_card(C) :-
card(C),
holds_none_of(Ns),
\+ member(C, Ns).
I have omitted who owns what and such. I have not normalized holds_some_of/1 and holds_none_of/1 on purpose.
This is actually enough for the following queries:
?- holds_card(X).
X = 1 ;
X = 4.
?- holds_card(1).
true.
?- holds_card(2).
false.
?- holds_card(3).
false.
?- holds_card(4).
true.
which comes to show that you don't even need the knowledge that Bob is holding 1 or 2. By the way, while trying to code this, I noticed the following ambiguity, from the original problem statement:
Alice asks Bob "Do you have a 1 or a 2?", in response to which Bob shows Alice a card. Charlie now learns that Bob owns a 1 or a 2.
Does that now mean that Bob has exactly one of 1 and 2, or that he could be holding either one or both of the cards?
PS
The small program above can actually be reduced to the following query:
?- member(C, [1,2,3,4]), \+ member(C, [2,3]).
C = 1 ;
C = 4.
(Eep, I just realized this is 6 years old, but maybe it's interesting to introduce logic-programming languages with probabilistic choices for the next stumbler )
I would say the accepted answer is the most correct, but if one is interested in probabilities, a PLP language such as problog might be interesting:
This example assumes we don't know how many cards bob holds. It can be modified for a fixed number of cards without much difficulty.
card(C):- between(1,5,C). % wlog: A world with 5 cards
% Assumption: We don't know how many cards bob owns. Adapting to a fixed number of cards isn't hard either
0.5::own(bob, C):-
card(C).
pos :- (own(bob,1); own(bob,2)).
neg :- (own(bob,2); own(bob,3)).
evidence(pos). % tells problog pos is true.
evidence(\+neg). % tells problog neg is not true.
query(own(bob,Z)).
Try it online: https://dtai.cs.kuleuven.be/problog/editor.html#task=prob&hash=5f28ffe6d59cae0421bb58bc892a5eb1
Although the semantics of problog are a bit harder to pick-up than prolog, I find this approach an interesting way of expressing the problem. The computation is also harder, but that's not necessarily something the user has to worry about.

Resources