Prolog beginner, combining rules question? - prolog

I'm trying to write a program thats based on these facts:
Boyle was born in 1627 and died in 1691.
Newton was born in 1642 and died in 1727.
(and so on)
I want to create a rule that determines if a person was alive during a specified year. Here is what I've come up with so far:
scientist(boyle, 1627, 1691).
scientist(newton, 1642, 1727).
alive_after(X) :- scientist(A, B, C), B < X.
alive_before(X) :- scientist(A, B, C), C > X.
alive_during(X, Year) :- alive_after(X), alive_before(X).
I believe that my first two rules are correct, but they don't seem to be working as intended when I combine them for my alive_during rule. When I run my code with this as my input:
alive_during(1628).
X = boyle
It doesn't work.
What am I missing here?

Prolog cannot unify variables that are hidden inside predicate bodies. There is no relationship between the A in alive_after/1 and the A in alive_before/1. Prolog actually told you that it didn't know what you were doing when it reported these warnings: `
|: alive_after(X) :- scientist(A, B, C), B < X.
Warning: user://2:19:
Singleton variables: [A,C]
|: alive_before(X) :- scientist(A, B, C), C > X.
Warning: user://2:23:
Singleton variables: [A,B]
|: alive_during(X, Year) :- alive_after(X), alive_before(X).
Warning: user://2:27:
Singleton variables: [Year]
It is extremely important that you read these messages as if they are errors, especially when you are new to Prolog!
The solution is to make it so that Prolog is able to unify the scientists across these predicates:
alive_after(Scientist, Year) :- scientist(Scientist, Birth, _), Year > Birth.
alive_before(Scientist, Year) :- scientist(Scientist, _, Death), Year < Death.
alive_during(Scientist, Year) :-
alive_before(Scientist, Year), alive_after(Scientist, Year).
You may also find that it is a little easier to follow the logic when you give your variables meaningful names. I am guilty of using extremely terse variable names when writing very general predicates, but these are actually very specific predicates and a good name can help you understand the structure of what you're doing. It would be, I think a little harder to see how this is more correct than what you wrote:
alive_after(A, X) :- scientist(A, B, _), X > B.
alive_before(A, X) :- scientist(A, _, C), X < C.
alive_during(A, X) :- alive_before(A, X), alive_after(A, X).
With better names, it is much easier to see why your original code is incorrect, because the scientist is not actually shared between the alive_before/2 and alive_after/2 calls.
Another hint that you were confused was that this response to a query makes no sense:
?- alive_during(1628).
X = boyle
Where did X come from? Variables get unified with values from the query, they do not arrive from inside predicate bodies.
An even more direct solution would be to use Prolog's built-in between/3 predicate:
alive_during(Scientist, Year) :-
scientist(Scientist, Birth, Death),
between(Birth, Death, Year).
This has the added advantage that it will actually generate solutions for you:
?- alive_during(boyle, X).
X = 1627 ;
X = 1628 ;
X = 1629 ;
X = 1630 ;
X = 1631 ;
The other solution does not have this property. There are interesting predicates you can write if you do have this generating property, such as contemporaries/2:
contemporaries(S1, S2) :-
alive_during(S1, Y),
alive_during(S2, Y),
dif(S1, S2).
Which generates a lot of not interesting copies of solutions, but you can get rid of them by using setof/3:
?- setof(X-Y, contemporaries(X, Y), Contemporaries).
Contemporaries = [boyle-newton, newton-boyle].

Related

Prolog predicate where two items are associated but not equivalent

I haven't programmed in Prolog for years and am struggling with a simple block of test code (I'm trying to solve a logic puzzle for fun...):
aboard(jack, blackbird).
aboard(jim, blackbird).
aboard(donna, blackbird).
aboard(david, north_star).
aboard(sandy, north_star).
shipmates(A, B) :- A \= B, aboard(A, X), aboard(B, X).
shipmates1(A, A) :- !, fail.
shipmates1(A, B) :- aboard(A, X), aboard(B, X).
The shipmates and shipmates1 rules are two different attempts to accomplish the following: I want to pair all passengers who are on the same ship but are not equivalent to each other.
For example, I want shipmates(jack, jack). to be false.
When I query this with fully-qualified arguments, I get the expected answers:
3 ?- shipmates(jack, david).
false.
4 ?- shipmates(jack, jack).
false.
5 ?- shipmates(jack, jim).
true.
However, when I want all of Donna's shipmates, it doesn't seem to work:
6 ?- shipmates(donna, X).
false.
I was expecting:
X = jack ;
X = jim ;
NOTE: I get the same wrong results with shipmates1.
So please take pity on a very amateur Prolog programmer (who is not doing homework for a class!) What very obvious thing am I doing wrong?
Version: SWI-Prolog (threaded, 64 bits, version 8.0.2)
Try:
shipmates(A, B) :-
aboard(A, X),
aboard(B, X),
A \= B.
By calling the aboard/2 predicate before the A \= B goal, you ensure that both A and B will be instantiated, thus making the comparison meaningful.

Depending on faliure to prove in prolog

Consider this code
:- use_module(library(clpfd)).
p(1).
p(3).
p(5).
p(7).
predecessor(A, B) :- A #= B - 1. % is true for pairs
q(X) :- predecessor(P, X), \+ p(P).
If I query ?- p(X) I correctly get the results
?- p(X).
X = 1 ;
X = 3 ;
X = 5 ;
X = 7.
But if I query ?- q(X) then I get false.
I realize that \+ is really not negation but faliure to prove, but what if not being able to prove something is sufficient for another predicate being true?
I wanted to give a reasonable use case / example which is why I resorted to using clpfd. Even without using it, I have another example which I can present:
likes(betty, butter).
likes(betty, jam) :- fail.
dislikes(betty, Item) :- \+ likes(betty, Item).
This example too, has a shortcoming that likes(betty, jam) :- fail. isn't really doing anything. But I hope I'm able to get my point across.
Is there a way in prolog to define this dependence?
You have to specifically define the "negative universe" of possibilities if you want Prolog to provide solutions in that space.
For instance, \+ p(X) cannot tell you specific values of X because the possible X that meet this criteria have not been defined. You're asking Prolog to invent what X might possibly be, which it cannot do.
You could define the universe of all possible values, then you can define what \+ p(X) means:
:- use_module(library(clpfd)).
p(1).
p(3).
p(5).
p(7).
predecessor(A, B) :- A #= B - 1. % is true for pairs
q(X) :- predecessor(P, X), P in 0..9, label([P]), \+ p(P).
Then you get:
2 ?- q(X).
X = 1 ;
X = 3 ;
X = 5 ;
X = 7 ;
X = 9 ;
X = 10.
3 ?-
Here we've told Prolog that the possible universe of P to choose from is defined by P in 0..9. Then the call \+ p(P) can yield specific results. Unfortunately, using \+, you still have to apply label([P]) before testing \+ p(P), but you get the idea.
In your other example of likes, it's the same issue. You defined:
likes(betty, butter).
likes(betty, jam) :- fail.
As you indicated, you wouldn't normally include likes(betty, jam) :- fail. since failure would already occur due to lack of a successful fact or predicate. But your inclusion is really an initial attempt to define the universe of possible choices. Without that definition, Prolog cannot "invent" what to pick from to test for a dislike. So a more complete solution would be:
person(jim).
person(sally).
person(betty).
person(joe).
food(jam).
food(butter).
food(eggs).
food(bread).
likes(betty, butter).
Then you can write:
dislikes(Person, Food) :-
person(Person),
food(Food),
\+ likes(Person, Food).

Can't show the second answer in Prolog

sisters(mary,catherine).
sisters(catherine,mary).
brothers(john,simone).
brothers(simone,john).
marriage(john,mary,2010).
marriage(mary,john,2010).
marriage(kate,simone,2009).
marriage(simone,kate,2009).
marriage(catherine,josh,2011).
marriage(josh,catherine,2011).
birth(mary,johnny).
birth(mary,peter).
birth(catherine,william).
birth(kate,betty).
givebirthyear(mary,peter,2015).
givebirthyear(mary,johnny,2012).
givebirthyear(catherine,william,2012).
givebirthyear(kate,betty,2011).
siblings(X,Y) :-
birth(Parent,X),
birth(Parent,Y).
cousins(X,Y) :-
birth(Xparent,X),
birth(Yparent,Y),
sisters(Xparent,Yparent).
cousins(X,Y) :-
X \= Y,
birth(Xmom,X),
birth(Ymom,Y),
marriage(Xmom,Xdad,_),
marriage(Ymom,Ydad,_),
brothers(Xdad,Ydad).
I don' know what's happening in my code. When I input
cousins(betty,johnny).
and
cousins(william,johnny).
The prolog says true. But when I entered
cousins(S,johnny).
THe prolog says S = william but didn't show me that S = betty. I don't really know what's happening. Need help.
Here is the prolog result I got.
?- cousins(S,johnny).
S = william ;
false.
?- cousins(betty,johnny).
true.
?- cousins(william,johnny).
true .
The problem
The reason this happens is because
X \= Y,
actually means:
\+(X = Y).
now \+ or not in Prolog has some weird behaviour compared to the logical not. \+ means negation as finite failure. This means that \+(G) is considered to be true in case Prolog queries G, and can not find a way to satisfy G, and that G is finite (eventually the quest to satisfy G ends).
Now if we query \+(X = Y), Prolog will thus aim to unify X and Y. In case X and Y are (ungrounded) variables, then X can be equal to Y. As a result X \= Y fails in case X and Y are free variables.
So basically we can either use another predicate that for instance puts a constraint on the two variables that is triggered when the variables are grounded, or we can reorder the body of the clause, such that X and Y are already grounded before we call X \= Y.
If we can make for instance the assumption that X and Y will be grounded after calling birth/2, we can reorder the clause to:
cousins(X,Y) :-
birth(Xmom,X),
birth(Ymom,Y),
X \= Y,
marriage(Xmom,Xdad,_),
marriage(Ymom,Ydad,_),
brothers(Xdad,Ydad).
Prolog has however a predicate dif/2 that puts a constraint on the two variables, and from the moment the two are grounded, it will fail if the two are equal. So we can use it like:
cousins(X,Y) :-
dif(X,Y),
birth(Xmom,X),
birth(Ymom,Y),
marriage(Xmom,Xdad,_),
marriage(Ymom,Ydad,_),
brothers(Xdad,Ydad).
Making things simpler
That being said, I think you make the program too complex. We can start with a few definitions:
two people are slibings/2 if they are brothers/2 or sisters/2.
slibings(X,Y) :-
brothers(X,Y).
slibings(X,Y) :-
sisters(X,Y).
It is however possible that brothers/2 and sisters/2 do not provide all information. Two people are also slibings if they have the same mother (we will assume that people do not divorce here, or at least not give birth to other children after they remarry).
slibings(X,Y) :-
dif(X,Y),
birth(Mother,X),
birth(Mother,Y).
a parent/2 of a person is the mother of the person or the father (the person that married the mother).
So we can write:
parent(Mother,X) :-
birth(Mother,X).
parent(Father,X) :-
birth(Mother,X),
marriage(Father,Mother,_).
based on your example, the marriage/3 predicate is bidirectional: in case marriage(X,Y,Z)., then there is also a fact marriage(Y,X,Z)..
And now we can define:
two people are cousins if there parents are slibings:
cousins(X,Y) :-
parent(MF1,X),
parent(MF2,Y),
slibings(MF1,MF2).
and that's it.

Prolog - deconstructing goals into individual facts and arithmetic

I'm attempting to write a small program that breaks up a given goal into all its smallest parts and eventually evaluates them. So far I have:
alien(X) :- fromMars(X), fromSaturn(X); fromJupiter(X), X = 'john'.
fromMars(john).
fromSaturn(john).
fromJupiter(john).
test(true) :- !.
test((Goal1,Goal2)) :- test(Goal1), test(Goal2).
test((Goal1;Goal2)) :- test(Goal1), test(Goal2).
test(X = Y) :- call(X = Y).
test(Goal) :- clause(Goal,Body),test(Body).
As far as I can tell so far, this will recursively inspect rules using clause/2 e.g. if I call test(alien(john)). . When it reaches the point that Body contains only facts such as fromMars(X), fromSaturn(X); fromJupiter(X), X = 'john' it will split those using the test((Goal1,Goal2)) :- and test((Goal1;Goal2)) :- rules, eventually reaching singular facts. When passed a singular fact, clause/2 will instantiate Body to true if it can be solved.
Problems arise with arithmetic. In the above program, eventually there will be the singular goal X = 'john' This causes an error with clause/2 (private procedure?). I introduced the rule test(X = Y) :- to catch this case so I can deal with it another way. However what I really want is a rule that will catch all arithmetic. Obviously I cant write a rule in the style of test(X = Y) :- to catch all possible types of arithmetic.
My goal is to eventually write an abductive meta-interpeter that can handle any type of rule thrown at it.
Let me know if none of this makes any sense and I'll try to clarify :)
meta interpreters it's one of the 'strong points' of Prolog. See this page from Markus Triska about this interesting theme.
As Little Booby Tables advised, you can capture arithmetic with something simple as
test(X is Y) :- X is Y.
BTW I think you have a typo here
test((Goal1;Goal2)) :- test(Goal1), test(Goal2).
should be
test((Goal1;Goal2)) :- test(Goal1) ; test(Goal2).
EDIT: working with operators can be generalized, if required. Just an example of the builtins required:
?- X = (1+2), X =.. [F, A, B], current_op(U, V, F).
X = 1+2,
F = (+),
A = 1,
B = 2,
U = 200,
V = fy .

SWI Prolog does not terminate

:- use_module(library(clpfd)).
fact(treated=A) :- A in 0..1.
fact(numYears=B) :- B in 0..sup.
fact(numDrugs=C) :- C in 0..sup.
fact(treated2=D) :- D in 0..1.
fact(cParam=E) :- E in 0..4.
is_differentfact(X,X) :- false.
is_differentfact(Element=_,OtherElement=_) :-
dif(Element,OtherElement).
is_fakt([]).
is_fakt([X|Xs]) :-
fact(X),
maplist(is_differentfact(X),Xs),
is_fakt(Xs).
Why does ?- is_fakt(X) return a list of results answers but after a number of results answers it hangs. I don't know why Prolog cannot return all possible values of X.
You ask:
Why does ?- is_fakt(L) ... but after a number of results answers it hangs.
You say a number. That number is 62 times pressing SPACE to get to that moment of looping. Pretty long isn't it? And your program is tiny. How will you ever get the chance to do the same with a bigger program? Don't worry, there is help. But you need to look at the program from a different angle.
In Prolog understanding the very precise execution of a concrete query is next to impossible. You have two different kinds of control flows interleaved plus strange data structures that do not need to be present, but "come in" later ; sometimes. All that opens up a veritable panoply of possible execution traces that are so full of detail, that your mind will overflow — worse: your mind will still pretend you understand everything but effectively you don't. And the bugs have big party time in your program. Those bugs will bite at some point in time in the future, but only on a bug-to-bite basis. That can be very demoralizing. After all, the program is so small, that should be easy to understand (by the standards of imperative languages). But then, Prolog programs tend to be very compact for problems that are very complex in other languages.
Try to step through with a tracer to see what I mean. You will see all kinds of things happening. And most of them are irrelevant.
Fortunately, there are ways to understand Prolog, but here you have to rely on nice properties of the language itself. For localizing reasons for non-termination, the best is to start to consider a failure-slice. You obtain a failure slice from your program by adding goals false into your program. If the resulting program then still does not terminate, we have a reason why also our original program does not terminate.
Think of it: instead of trying to understand your program we do something humans are much better at: Making an educated guess. That guess can go wrong but we can check that easily. In the beginning you will be pretty awful at guessing. Soon you will see that you can do a lot of things systematically. All code that now becomes irrelevant is stike through.
:- use_module(library(clpfd)).
fact(treated=A) :- A in 0..1.
fact(numYears=B) :- B in 0..sup, false.
fact(numDrugs=C) :- C in 0..sup, false.
fact(treated2=D) :- D in 0..1, false.
fact(cParam=E) :- E in 0..4, false.
is_differentfact(X,X) :- false.
is_differentfact(Element=_,OtherElement=_) :-
dif(Element,OtherElement).
is_fakt([]).
is_fakt([X|Xs]) :-
fact(X),
maplist(is_differentfact(X),Xs),
is_fakt(Xs).
What did we gain? We can narrow down the problem much faster:
?- is_fakt(Xs).
Xs = []
; Xs = [treated=_A], _A in 0..1
; loops.
Before continuing, I try to understand what you mean with is_fakt/1. You probably mean: All the facts by their name, and make sure none is repeated. Now we have only the fact named treated, so we can only produce a list of length 1. And then it loops.
You said:
I don't know why Prolog cannot return all possible values of X.
To be picky, that is not true. Prolog did enumerate all possible values of X. But then it did not terminate.
((Some remarks to consider: Do you really want to get that list in that manner? You will get all permutations! With a list of length n you will get n! different answers. For n = 10 that is 3628800. Is this, what you want? Probably not.))
But let us first stick to identify the precise reason for non-termination.
To better identify the reason, lets "turn off" all answers. So we query is_fakt(L), false instead with:
:- use_module(library(clpfd)).
fact(treated=A) :- A in 0..1.
fact(numYears=B) :- B in 0..sup, false.
fact(numDrugs=C) :- C in 0..sup, false.
fact(treated2=D) :- D in 0..1, false.
fact(cParam=E) :- E in 0..4, false.
is_differentfact(X,X) :- false.
is_differentfact(Element=_,OtherElement=_) :-
dif(Element,OtherElement).
is_fakt([]) :- false.
is_fakt([X|Xs]) :-
fact(X),
maplist(is_differentfact(X),Xs), false,
is_fakt(Xs).
That is a minimal failure-slice. So it is the maplist/2 which does not terminate in the first place. Your idea was to ensure that X has a fact-name that is different to the fact-names in Xs. But if Xs is not bound, that will never terminate. Let's try it:
?- maplist(is_differentfact(X),Xs).
Xs = []
; X = (_A=_B), Xs = [_C=_D], dif(_A,_C)
; X = (_A=_B), Xs = [_C=_D,_E=_F], dif(_A,_C), dif(_A,_E)
; X = (_A=_B), Xs = [_C=_D,_E=_F,_G=_H],
dif(_A,_C), dif(_A,_E), dif(_A,_G)
; X = (_A=_B), Xs = [_C=_D,_E=_F,_G=_H,_I=_J],
dif(_A,_C), dif(_A,_E), dif(_A,_G), dif(_A,_I)
; X = (_A=_B), Xs = [_C=_D,_E=_F,_G=_H,_I=_J,_K=_L],
dif(_A,_C), dif(_A,_E), dif(_A,_G), dif(_A,_I), dif(_A,_K)
; ... .
Not so nice to look at... but we can do it better:
?- maplist(is_differentfact(X),Xs), false.
loops.
So it loops. This is the reason for non-termination. To fix the problem we have to do something in the remaining visible part of the failure slice...
For more, look up other explanations tagged failure-slice
Edited version based on the comments of false.
:- use_module(library(clpfd)).
:- use_module(library(lists)).
fact(treated-X) :- X in 0..1.
fact(numYears-X) :- X in 0..sup.
fact(numDrugs-X) :- X in 0..sup.
fact(treated2-X) :- X in 0..1.
fact(cParam-X) :- X in 0..4.
facts(Facts) :-
findall(X,fact(X),Facts).
is_fact2(_, []).
is_fact2(Facts, [X|Xs]) :-
member(X,Facts),
select(X,Facts,Remaining),
is_fact2(Remaining,Xs).
is_fakt(X) :-
facts(Facts),
is_fact2(Facts,X),
keysort(X,X).
This terminates now.

Resources