find all pair of grandfather relationships using prolog - prolog

If you have these facts:
parent(albert, bob).
parent(albert, betsy).
parent(albert, bill).
parent(alice, bob).
parent(alice, betsy).
parent(alice, bill).
parent(bob, carl).
parent(bob, charlie).
And then this code:
grand_parent(X, Y) :-
parent(Z, X),
parent(Y, Z).
How to write a query to find all pairs that share
grand-parenthood relationship?

A basic query would look like this:
?- grand_parent(X,Y).
giving the following output (after one answer ; will show the next one):
X = carl,
Y = albert;
X = carl,
Y = alice;
X = charlie,
Y = albert;
X = charlie,
Y = alice;
false.
However if you want to have all grandchildren of a specific person, you can either programm your own "find_all_grandchilren"-predicate or just use an inbuild predicate for collecting all answers. Example:
?- findall(Y,grand_parent(carl,Y),X).
X = [albert, alice].
Which reads as: find every term Y which satisfies grand_parent(carl,Y) and put it in the list X.
Tested with SWISH.

Getting the data model right is important.
You might find it useful to change your data model up to better represent the real-world model. Something like
parents( carol, bob, jim ).
Which is to say, carol and bob are respectively, jim's mother and father.
Once you have that things fall into place:
mother(M,C) :- parents(M,_,C).
father(F,C) :- parents(_,F,C).
child(P,C) :- mother(P,C).
child(P,C) :- father(P,C).
child(M,F,C) :- parents(M,F,C).
children(P,Cs) :- findall( C , child(P,C), Cs).
children(M,F,Cs) :- findall( C, child(M,F,C), Cs).
family(M,F,[M,F|Cs]) :- children(M,F,Cs).
sibling(S,C) :- parents(M,F,C), parents(M,F,S).
grandparents(GM,GF,C) :- mother(M,C), parents(GM,GF,M).
grandparents(GM,GF,C) :- father(F,C), parents(GM,GF,F).
Now, to get the list of all grandparent pairs is trivial:
all_grandparents(GPs) :- findall( GM:GF, grandparents(GM,GF,_), GPs ).

Related

prolog returns value twice

I am just learning prolog and I ran into the problem where I get the same answer twice when asking for niece and nephew. I just cannot wrap my head around it. Does anyone know what I am doing wrong? Perhaps it is something having two rules for each cousin? But it works fine when asking for brother or sister..
UPDATE: I tried out some things, and after returning to the code I wrote down here, the rule of a niece being only a female doesn't work anymore. and the nephew returns false.
So I have the code:
male( ted ).
male( john ).
male( aron ).
male( nico ).
male( gio ).
female( liza ).
female( sophie ).
female( lily ).
female( elin ).
female( daisy ).
parent_of( lily , aron ).
parent_of( john , aron ).
parent_of( john , elin ).
parent_of( lily , elin ).
parent_of( sophie , gio ).
parent_of( sophie , nico ).
parent_of( daisy , liza ).
parent_of( ted , liza ).
father_of(X,Y):- male(X),
parent_of(X,Y).
mother_of(X,Y):- female(X),
parent_of(X,Y).
sister_of( X , Y ) :- % (X,Y or Y,X) %
female(X),
father_of(F,Y),
father_of(F,X),
X \= Y.
sister_of( X , Y ) :-
female(X),
mother_of(M,Y),
mother_of(M,X),
X \= Y.
brother_of(X,Y):- %(X,Y or Y,X)%
male(X),
father_of(F,Y),
father_of(F,X),
X \= Y.
brother_of(X,Y):- male(X),
mother_of(M,Y),
mother_of(M,X),
X \= Y.
niece_of(X,Y):-
dif(X,Y),
female(X),
parent_of(W,Y),
parent_of(P,X),
brother_of(P,W).
niece_of(X,Y) :-
dif(X,Y),female(X),
parent_of(W,Y),
parent_of(P,X),
sister_of(P,W).
nephew_of(X,Y) :-
dif(X,Y),
male(X),
parent_of(W,Y),
parent_of(P,X),
brother_of(P,W).
nephew_of(X,Y):-
dif(X,Y),
male(X),
parent_of(W,Y),
parent_of(D,X),
sister_of(D,W).
The output I get is
?- niece_of(X,liza).
X = aron ;
X = aron ;
X = nico ;
X = nico ;
X = gio ;
X = gio ;
false.
?- nephew_of(X,liza).
false.
First of all, according to your database, there is no one who is anyone's niece or nephew. This can be clearly seen when you draw the graph for the relation parent_of/2.
female(lily).
female(sophie).
female(daisy).
female(elin).
female(liza).
male(john).
male(ted).
male(aron).
male(gio).
male(nico).
parent_of(lily, aron).
parent_of(lily, elin).
parent_of(john, aron).
parent_of(john, elin).
parent_of(sophie, gio).
parent_of(sophie, nico).
parent_of(daisy, liza).
parent_of(ted, liza).
Now, consider a first version of a rule that defines the relation siblings0/2:
siblings0(Person1, Person2) :-
dif(Person1, Person2),
parent_of(Parent, Person1),
parent_of(Parent, Person2).
Using this rule, there are two distinct ways to prove that Aron and Elin are siblings (first, because they both have the same mother; second, because they both have the same father). However, there is only one way to prove that Gio and Nico are siblings (since there is no information on who is the father of each one of them).
?- siblings0(aron, elin).
true ; % <== same mother
true ; % <== same father
false.
?- siblings0(gio, nico).
true. % <== same mother
To avoid redundancy, we can use the the ISO built-in predicate once/1:
siblings(Person1, Person2) :-
dif(Person1, Person2),
person(Person1),
person(Person2),
once( ( parent_of(Parent, Person1),
parent_of(Parent, Person2) ) ).
person(Person) :-
( female(Person)
; male(Person) ).
Now, we get only one answer for each query:
?- siblings(aron, elin).
true.
?- siblings(gio, nico).
true.
Thus, extending your database and using the predicate siblings/2 to define other relations, everything will work fine!
female(lily).
female(sophie).
female(daisy).
female(elin).
female(liza).
female(pat). % <== add
male(john).
male(ted).
male(aron).
male(gio).
male(nico).
male(coy). % <== add
parent_of(lily, aron).
parent_of(lily, elin).
parent_of(john, aron).
parent_of(john, elin).
parent_of(sophie, gio).
parent_of(sophie, nico).
parent_of(daisy, liza).
parent_of(ted, liza).
parent_of(elin, pat ). % <== add
parent_of(elin, coy). % <== add
parent_of(gio, pat). % <== add
parent_of(gio, coy). % <== add
siblings(Person1, Person2) :-
dif(Person1, Person2),
once( ( parent_of(Parent, Person1),
parent_of(Parent, Person2) ) ).
mother_of(Mother, Child):-
female(Mother),
parent_of(Mother, Child).
father_of(Father, Child):-
male(Father),
parent_of(Father, Child).
sister_of(Sister, Person) :-
dif(Sister, Person),
female(Sister),
siblings(Sister, Person).
brother_of(Brother, Person) :-
dif(Brother, Person),
male(Brother),
siblings(Brother, Person).
niece_of(Niece, Person):-
female(Niece),
parent_of(Parent, Niece),
siblings(Parent, Person).
nephew_of(Nephew, Person):-
male(Nephew),
parent_of(Parent, Nephew),
siblings(Parent, Person).
Examples:
?- niece_of(Niece, Person).
Niece = pat,
Person = aron ;
Niece = pat,
Person = nico.
?- nephew_of(Nephew, Person).
Nephew = coy,
Person = aron ;
Nephew = coy,
Person = nico.
First of all some simplification and extended family tree
male(ted).
male(john).
male(aron).
male(nico).
male(gio).
male(ken).
male(vladimir).
female(liza).
female(sophie).
female(lily).
female(elin).
female(daisy).
female(barbie).
parent(lily, aron).
parent(john, aron).
parent(john, elin).
parent(lily, elin).
parent(sophie, gio).
parent(sophie, nico).
parent(vladimir, gio).
parent(vladimir, nico).
parent(daisy, liza).
parent(ted, liza).
% grandfathers
parent(ken, lily).
parent(ken, daisy).
parent(ken, sophie).
parent(barbie, lily).
parent(barbie, daisy).
parent(barbie, sophie).
father(Father, Child) :-
male(Father),
parent(Father, Child).
mother(Mother, Child) :-
female(Mother),
parent(Mother, Child).
sibling(X, Y) :-
father(Father, Y),
father(Father, X),
X\=Y.
sister(Sister, Sibling) :-
female(Sister),
sibling(Sister, Sibling).
brother(Brother, Sibling) :-
male(Brother),
sibling(Brother, Sibling).
niece(Niece, ParentsSibling) :-
female(Niece),
parent(NiecesParent, Niece),
sibling(NiecesParent, ParentsSibling).
nephew(Nephew, ParentsSibling) :-
male(Nephew),
parent(NephewParent, Nephew),
sibling(NephewParent, ParentsSibling).
I've added sibling/2 predicate to remove some reused code.
Why did you have duplications on nephew?
You did use the parent predicate there when trying to define the sister of the nephew's parent, in that case, you would get each sibling twice (once because of evaluation with mother and once because of evaluating with father). You can verify that with trace\0.
Workaround
When looking for siblings I'm checking for 2 people with the same father (check sibling/2 predicate).
If for some reason your relations would be able to have situations that human beings can exist without fathers it won't work. In that case, you would need to modify sibling/2 predicate and use parent\2, produce all values (with findall for example) and then return distinct values.

How to find Nephew in Prolog Family Code?

I'm trying to get Prolog to output people in the family trees nephew, as in nephew(X,Y) will list
X = the nephew
Y = Aunt/ Uncle
I've tried writing some of the code already, I'm pretty confident the son command works, and I believe sibling works, however combining these is proving, difficult.
parent(pam,bob).
parent(john,bob).
parent(john,liz).
parent(bob,ann).
parent(bob,pat).
parent(pat,jim).
parent(liz,joe).
parent(liz,tim).
parent(joe,kim).
female(pam).
female(liz).
female(ann).
female(pat).
female(zoe).
female(kim).
male(bob).
male(john).
male(jim).
male(joe).
male(tim).
sibling(X, Y) :- parent(Z, X), parent(Z, Y), X \= Y.
son(X,Y) :- parent(Y, X), male(X).
nephew(X, Y) :- sibling(Y, Z), son(Z, X).
You were so close.
If you ran your query
nephew(X,Y) you would get
?- nephew(X,Y).
X = pam,
Y = liz ;
X = john,
Y = liz ;
X = liz,
Y = joe ;
X = liz,
Y = tim ;
false.
which as you note is wrong.
However if you rename your variables so that they are easier to comprehend
sibling(Child_a, Child_b) :-
parent(Parent, Child_a),
parent(Parent, Child_b),
Child_a \= Child_b.
son(Child,Parent) :-
parent(Parent, Child),
male(Child).
nephew(Parent, Child_a) :-
sibling(Child_a, Child_b),
son(Child_b, Parent).
you will see that sibling/2 and son/2 are correct and should see your problem with nephew/2.
When renaming variables, start at the facts and work back, do not start at the head of a clause and work down.
Another way that helps to figure out problems with multi-statement predicates is to run parts of them as separate queries, e.g.
?- sibling(Parent,Aunt_uncle),son(Nephew,Parent).
Parent = liz,
Aunt_uncle = bob,
Nephew = joe ;
Parent = liz,
Aunt_uncle = bob,
Nephew = tim ;
Parent = pat,
Aunt_uncle = ann,
Nephew = jim ;
false.
While this query gives more information than is needed, the correct values are in the results. Putting this in a predicate and returning only the desired values is all that is left to get this query working as desired.
A correct answer for nephew/2 is
nephew(Nephew,Aunt_uncle) :-
sibling(Parent,Aunt_uncle),
son(Nephew,Parent).
Example run:
?- nephew(Nephew,Aunt_uncle).
Nephew = jim,
Aunt_uncle = ann ;
Nephew = joe,
Aunt_uncle = bob ;
Nephew = tim,
Aunt_uncle = bob ;
false.
When writing code it is advisable to also create test cases.
If you are using SWI-Prolog then you can use the following test cases.
:- begin_tests(family).
% example of single test case
test(001) :-
sibling(bob,liz).
test(002) :-
son(tim,liz).
test(003,[nondet]) :-
nephew(tim,bob).
% example of test cases that test multiple variations for one predicate
sibling_test_case(bob,liz).
sibling_test_case(liz,bob).
sibling_test_case(ann,pat).
sibling_test_case(pat,ann).
sibling_test_case(joe,tim).
sibling_test_case(tim,joe).
test(004, [forall(sibling_test_case(Child_a,Child_b))]) :-
sibling(Child_a,Child_b).
son_test_case(bob,pam).
son_test_case(bob,john).
son_test_case(jim,pat).
son_test_case(joe,liz).
son_test_case(tim,liz).
test(005, [forall(son_test_case(Child,Parent)),nondet]) :-
son(Child,Parent).
nephew_test_case(joe,bob).
nephew_test_case(tim,bob).
nephew_test_case(jim,ann).
test(006, [forall(nephew_test_case(Nephew,Aunt_uncle)),nondet]) :-
nephew(Nephew,Aunt_uncle).
:- end_tests(family).
These are run with run_tests.
?- run_tests.
% PL-Unit: family ................. done
% All 17 tests passed
true.
See: Prolog Unit Tests

Creating a Niece Rule in Prolog

Using a family database, I need to create a niece rule (niece(X,Y))in swi-prolog which is defined as "X is a niece of Y if X is a daughter of Y's brother or sister." This is the given database with my already designed rules:
% family DB
grandfather(don,who).
father(don,ted).
father(don,barb).
father(don,paula).
father(greg,erin).
father(greg,austin).
father(wes,alyssa).
father(ted,jessica).
father(ted,david).
%mother(ted, john).
mother(audrey,ted).
mother(audrey,barb).
mother(audrey,paula).
mother(paula,erin).
mother(paula,austin).
mother(barb,alyssa).
married(don,audrey).
married(wes,barb).
married(greg,paula).
male(don).
male(ted).
male(wes).
male(greg).
male(austin).
male(david).
female(audrey).
female(barb).
female(paula).
female(alyssa).
female(jessica).
female(erin).
parent(X,Y) :-
father(X,Y)
; mother(X,Y).
grandfather(X,Y) :-
father(X,Z),
( father(Z,Y)
; mother(Z,Y)
).
samefather(X,Y) :-
father(F,X),
father(F,Y).
samemother(X,Y) :-
mother(M,X),
mother(M,Y).
sameparent(X,Y) :-
samefather(X,Y).
sameparent(X,Y) :-
samemother(X,Y),
not(samefather(X,Y)).
couple(X,Y) :-
married(X,Y),
married(X,Y).
Here is my initial try at the niece rule:
niece(X,Y) :-
parent(F,X),
sameparent(Y,F).
My idea is to use the sameparent rule to check if Y and F are siblings and then check if F is the parent of X. This rule currently doesn't work. I'm still struggling to understand the syntax of combining multiple rules. If anyone could help me by using this same logic, it would be greatly appreciated.
Removing unnecessary rules :
parent(don,ted).
parent(don,barb).
parent(don,paula).
parent(greg,erin).
parent(greg,austin).
parent(wes,alyssa).
parent(ted,jessica).
parent(ted,david).
parent(audrey,ted).
parent(audrey,barb).
parent(audrey,paula).
parent(paula,erin).
parent(paula,austin).
parent(barb,alyssa).
male(don).
male(ted).
male(wes).
male(greg).
male(austin).
male(david).
female(audrey).
female(barb).
female(paula).
female(alyssa).
female(jessica).
female(erin).
father(X,Y) :-
male(X),
parent(X,Y).
mother(X,Y) :-
female(X),
parent(X,Y).
sameparent(X,Y) :-
parent(A,X),
parent(A,Y).
Gives you :
niece(X,Y) :-
female(X),
parent(Z,X),
sameparent(Z,Y),
Z \= Y.
Meaning line by line :
X is a niece of Y if
X is a female and
one parent of X
has a parent in common with Y
who isn't himself/herself.
This gives you :
| ?- niece(X,Y).
X = alyssa
Y = ted;
X = alyssa
Y = paula;
X = jessica
Y = barb;
X = jessica
Y = paula;
X = erin
Y = ted;
X = erin
Y = barb
Twice because in the case of your database every brothers/sisters share the same two parents.
For example, if A is the daughter of B and B is the half-brother of C, A is still the niece of C.
If you want that rule to be false and A to be the niece of C only if B and C are brothers/sisters (and not only half), you can change the niece rule to the following :
sameFather(X,Y) :-
father(A,X),
father(A,Y).
sameMother(X,Y) :-
mother(A,X),
mother(A,Y).
niece(X,Y) :-
female(X),
parent(Z,X),
sameFather(Z,Y),
sameMother(Z,Y),
Z \= Y.
Then you won't get duplicate results whith niece(X,Y).
For me it worked well.
sameparent(X,Y):- parent(A,X),parent(A,Y).
niece(X,Y) :- female(X),parent(Z,X),sameparent(Z,Y),Z=Y.

How to check if any statisfying clause exists in Prolog without backtracking through all different paths?

Let's say I have the following:
parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).
% people are siblings of each other if they share a parent
% and aren't the same person.
sibling(A, B) :-
parent(X, A),
parent(X, B),
B \= A.
Now if I ask for the siblings of Diane, I get Charlie and Eve — twice, once through Bob and once through Alice. I only want each once.
I don't think I can use a cut here, as that would prevent backtracking altogether. What I would like, is a way to check if any exists.
Paraphrasing
sibling(A, B) :-
∃(parent(X, A), parent(X, B)),
B \= A.
I tried several cuts, but none worked.
I tried findall/3 on (parent(X, A), parent(X, B)) and checking if the resulting list is non-empty, but that doesn't unify A or B.
Using setof/3 as suggested below works, but I really want to find a way to incorporate it in the definition of sibling/2, instead of having to use it in the question. I'd really like to be able to do the following:
?- sibling(diane, X).
X = charlie ;
X = eve ;
false.
or this
?sibling(X, Y).
X = charlie,
Y = diane ;
X = charlie,
Y = eve ;
X = diane,
Y = charlie ;
X = diane,
Y = eve ;
X = eve,
Y = charlie ;
X = eve,
Y = diane ;
false.
Like I said below, I have a solution for this specific case. What I would like, and what I'm setting a bounty for, is a general solution.
Instead of
sibling(A, B) :-
setof(D, X^(parent(X, A), parent(X, D)), Ds),
member(B, Ds),
B \= A.
I'd like to do
sibling(A, B) :-
exists(X^(parent(X, A), parent(X, B))),
B \= A.
which unifies A and B.
How do I define exists/1?
Using cut in Prolog is very delicate. Most cuts are essentially incorrect, but work still in certain situations. You could use a cut here, provided you want exactly one answer. But since you want the entire set, you are out of luck: You need to explore all answers to determine that set.
Fortunately, there is an elegant shortcut (pun intended) for that: setof/3. So ask
?- setof(t, sibling(diane, S), _).
For this usage of setof/3, the last argument is of no interest. It is actually [t].
For a general purpose exists/1, define
exists(XGoal) :- setof(t, XGoal, _).
This allows the use of existential quantifiers.
Here is a version using only the cut predicate !/0:
person(alice).
person(bob).
person(charlie).
person(diane).
person(eve).
parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).
sibling(X, Y) :-
person(X),
person(Y),
X \= Y,
sameparent(X, Y).
sameparent(X, Y) :-
parent(P, X),
parent(P, Y),
!.
We do not put the predicate !/0 in the predicate sibling/2 to allow backtracking to the goal sibling(X, Y) after the first common parent has been found. Instead we put the predicate !/0 in a new predicate sameparent/2. We also add a person predicate because the goal X \= Y requires its X and Y to be instantiated. See this document for more information.
A query:
?- sibling(diane, Y).
Y = charlie
Y = eve
Another query:
?- sibling(X, Y).
X = charlie,
Y = diane
X = charlie,
Y = eve
X = diane,
Y = charlie
X = diane,
Y = eve
X = eve,
Y = charlie
X = eve,
Y = diane
parent(alice, charlie).
parent(bob, charlie).
parent(bob, diane).
parent(alice, diane).
parent(bob, eve).
parent(alice, eve).
% people are siblings of each other if they share a parent
% and aren't the same person.
sibling(A, B) :-
setof(D, X^(parent(X, A), parent(X, D)), Ds),
member(B, Ds),
B \= A.
?- sibling(X, Y).
X = charlie,
Y = diane ;
X = charlie,
Y = eve ;
X = diane,
Y = charlie ;
X = diane,
Y = eve ;
X = eve,
Y = charlie ;
X = eve,
Y = diane ;
false.
Now I'm wondering how to extract this to a method exists/1, for general use.
So, as seen in the accepted answer, we can run
?- setof(P, (parent(P,diane), parent(P,X), X\=diane), _).
or
?- setof(P, (parent(P,diane), parent(P,X)), _), X\=diane.
to get what you want.
Hence we can define a binary predicate "exists A such that B holds":
exists(A, B):- setof( A, B, _).
and use it as
sibling_v1(A, B):- exists( P, (parent(P,A), parent(P,B)) ), B \= A.
or as
sibling_v2(A, B):- exists( P, (parent(P,A), parent(P,B), B \= A) ).
to define the desired predicates.
But it'll still run through all the possible paths to find out all the possible solutions (just reporting each once). For how could it be certain to have not missed some, otherwise?
57 ?- exists( P, (parent(P,diane), parent(P,X), X\=diane)).
X = charlie ;
X = eve.
58 ?- exists( P, (parent(P,diane), parent(P,X), writeln([P,X]), X\=diane)).
[bob,charlie]
[bob,diane]
[bob,eve]
[alice,charlie]
[alice,diane]
[alice,eve]
X = charlie ;
X = eve.
59 ?-

Prolog Family Tree query issues

% facts
mother(john, dana).
father(john, david).
mother(chelsea, dana).
father(chelsea, david).
mother(jared, dana).
father(jared, david).
% queries
father(X,Y) :- father(X,Y), write(Y).
mother(X,Y) :- mother(X,Y), write(Y).
parent(X,Y) :- father(X,Y);mother(X,Y).
sibling(X,Y) :- parent(X,Z), parent(Y,Z), write(Y).
I am having trouble getting these queries to work. when I type in the father command, it will tell me yes or no correctly, but won't do the write command (same with mother). "parent" doesn't work at all for me (therefor sibling doesn't either). Also, if I type in sibling(X,Y). I need to get all siblings...for example, sibling(john, chelsea). I need to output all the possible siblings (jared as well). Let me know where I am going wrong, I really don't see an issue with my logic here. Thanks!
Basically you can remove your mother and father predicates that are not facts. They are infinite loops. Since parent use them and sibling use parent, all your predicates are infinite loops.
To see what happens, you can do that :
?- trace, father(john, X).
and observe how prolog handles the query. You'll soon observe than to resolve father, he needs to solve father, and that to solve father, he needs to solve father, and that it never stops...
When the two problematic are removed, I obtain a correct behaviour :
?- father(john, X).
X = david.
?- parent(john, X).
X = david ;
X = dana.
?- sibling(john, X).
john
X = john ;
chelsea
X = chelsea ;
jared
X = jared ;
john
X = john ;
chelsea
X = chelsea ;
jared
X = jared.
Now, to make your sibling predicate better, you could say that someone is not its own sibling and that if you have one common parent it's enough (it will remove the duplicates) :
sibling(X,Y) :- father(Y,Z), father(X, Z), X =\= Y.

Resources