Prolog - family tree - prolog

I am working on creating a family tree in prolog. Where I am running into trouble is when I call on sister or brother. The results I get are correct, where julie is the sister of mike, julie is the sister of amanda, amanda is the sister of mike and amanda is the sister of julie. But what happens rather than ending there, if i keep hitting the 'n' key it will loop back through the results again. Why is this happening?
parent(pete,mike).
parent(pete,julie).
parent(pete,amanda).
parent(mary,mike).
parent(mary,julie).
parent(mary,amanda).
female(mary).
female(julie).
female(amanda).
male(mike).
male(pete).
mother(X,Y):-
parent(X,Y),
female(X).
father(X,Y):-
parent(X,Y),
male(X).
sibling(X,Y):-
parent(Z,X),
parent(Z,Y),
X\=Y.
sister(X,Y):-
sibling(X,Y),
female(X).
brother(X,Y):-
sibling(X,Y),
male(X).

Every pair of siblings will have both the same father and the same mother (in the more conservative societies, at least, and in your program at the moment). This is where you get the double answers from. You could maybe say that siblings always share both parents?

As stated, you get double answers because you're checking if they have the same parent, and they have two of those. Moreover, you're getting even more solutions because e.g. mike is sibling of amanda, but also amanda is sibling of mike, and you do nothing to prevent both solutions to appear.
So instead of:
sibling(X, Y):-
parent(Z, X),
parent(Z, Y),
X\=Y.
to solve problem of two parents, you could say they always share both parents:
sibling(X, Y):-
mother(Z, X),
mother(Z, Y),
father(W, X),
father(W, Y),
X\=Y.
You could stop second problem (X=mike, Y=amanda; X=amanda, Y=mike) by introducing #<:
sibling(X, Y):-
mother(Z, X),
mother(Z, Y),
father(W, X),
father(W, Y),
X#<Y.
This way, only one set of solutions is appearing, but you have to be careful with it, as it might not get you want you want, depending on what you want to achieve (X=julie, Y=amanda would not be true in this case).
Depending on the need, perhaps more elegant and general solution (not requiring both parents to be same) would be to use setof (http://www.swi-prolog.org/pldoc/doc_for?object=setof/3) with your original sibling clause, but you would rework your brother/sister clauses, e.g.:
sibling(X, Y):-
parent(Z, X),
parent(Z, Y),
X\=Y.
sister(X,Y,L):-
female(X),
setof([X, Y], (sibling(X, Y)), L).
brother(X,Y,L):-
male(X),
setof([X, Y], (sibling(X, Y)), L).
This would get you a list of unique pairs in list L, e.g. for sister(X, Y, L) you get:
X = julie
L = [[julie, amanda], [julie, mike]]
X = amanda
L = [[amanda, julie], [amanda, mike]]
Play with it a little to get exactly what you need.

Related

Function not in prolog

sibling(X, Y):- father(Z, X), father(Z, Y), not (X=Y).
sister(X, Y):- father(Z, X), father(Z, Y), female(X).
brother(X, Y):- father(Z, X), father(Z, Y), male(X).
i'm having a bit problem with using the not function. i've tried not X=Y. but to no avail, the sibling rule still produce error.
if i were to delete the not x=y, the output will be a bit kind of "ugly".
how should i write the not function?
The ISO predicate implementing not provable is called (\+)/1.
However, as #coder explains in the comments, it is much better to use dif/2 to express that two terms are different.
dif/2 is a pure predicate that works correctly in all directions, also if its arguments are not yet instantiated.
For example, with (\+)/1, we get:
?- \+ (X = Y ).
false.
No X and Y exist that satisfy this goal, right? Wrong:
?- X = a, Y = b, \+ (X = Y ).
X = a,
Y = b.
In contrast, with dif/2:
?- dif(X, Y).
dif(X, Y).
and in particular:
?- X = a, Y = b, dif(X, Y).
X = a,
Y = b.
See prolog-dif for more information. dif/2 is with us since the very first Prolog system. I strongly recommend you use it.
SWI Prolog has no notoperator. it can be used as a regular compound term, e.i. not(X).
It must be no space between functor and open parenthesis:
foo( argument list ).
This is the cause of the error.
SWI Prolog suggests ISO-standard replacement for not/1: (\+)/1

Who has a brother who is grand dad?

I am trying to query who has a brother that is a grandad but cannot figure it out, I know its simple but its confusing me a little.
I have some basic facts here:
parent(queenmother,elisabeth). parent(elisabeth,charles).
parent(elisabeth,andrew). parent(elisabeth,anne).
parent(elisabeth,edward). parent(diana,william).
parent(diana,harry). parent(sarah,beatrice).
parent(anne,peter). parent(anne,zara).
parent(george,elisabeth). parent(philip,charles).
parent(philip,andrew). parent(philip,edward)...
I have some predicates that determins male from female:
the_royal_males(charles). the_royal_males(andrew).
the_royal_males(edward). the_royal_males(william).
the_royal_males(philip). the_royal_males(phillip).
the_royal_males(georgejun). the_royal_males(harry).
and to determin ancestors, brothers, siblings.
ancestor(X,Y):- parent(X,Y).
ancestor(X,Y):- parent(X,Z), ancestor(Z,Y).
sibling(X,Y):- parent(Z,X), parent(Z,Y), X \= Y.
brother(X,Y):- sibling(X,Y), sibling(Y,X), the_royal_males(Y), X \= Y.
The queries i've been trying such as
brother(X,_), ancestor(X,_)
Isn't giving the desired results. Any help/guidance would be great. Thanks!

Simple prolog program returns false too early

It's been a while since I've programmed in Prolog. Today, I tried to make a simple program. It lists some facts of who belongs to the same family. If two people belong to the same family, they cannot give eachother gifts. I want to get all the people (or at least one person) to whom someone is allowed to give a gift.
family(john, jack).
family(matt, ann).
family(ann, jack).
family(jordan, michael).
family(michael, liz).
sameFamily(X, Y) :-
family(X, Y).
sameFamily(X, X) :-
false.
sameFamilySym(X, Y) :-
sameFamily(X, Y).
sameFamilySym(X, Y) :-
sameFamily(Y, X).
sameFamilyTrans(X, Z) :-
sameFamilySym(X, Y),
sameFamilySym(Y, Z).
gift(X, Y) :-
not(sameFamilyTrans(X, Y)).
Some queries if sameFamilyTrans/2 return false when they should in fact return true.
sameFamilyTrans/2 is obviously wrong. I think I need to keep a list of intermediate transitivities. Something like this:
sameFamilyTrans(X, Z, [Y|Ys]) :-
sameFamilySym(X, Y, []),
sameFamilyTrans(Y, Z, Ys).
But then I don't know how to call this.
P.S. I am using SWI-Prolog, if that makes any difference.
Yes, you were on the right track. The trick is to call the transitive closure with an empty accumulator, and check in each step whether a cycle is found (i.e., whether we have seen this member of the family before. As "false" has pointed out, the persons need to be instantiated already before going into the not, though.
So in sum, this works:
family(john, jack).
family(matt, ann).
family(ann, jack).
family(jordan, michael).
family(michael, liz).
sameFamily(X, Y) :-
family(X, Y).
sameFamilySym(X, Y) :-
sameFamily(X, Y).
sameFamilySym(X, Y) :-
sameFamily(Y, X).
sameFamilyTrans(X, Y, Acc) :-
sameFamilySym(X, Y),
not(member(Y,Acc)).
sameFamilyTrans(X, Z, Acc) :-
sameFamilySym(X, Y),
not(member(Y,Acc)),
sameFamilyTrans(Y, Z, [X|Acc]).
person(X) :- family(X, _).
person(X) :- family(_, X).
gift(X, Y) :-
person(X),
person(Y),
X \= Y,
not(sameFamilyTrans(X, Y, [])).
A bit of background: Transitive closure is not actually first-order definable (cf. https://en.wikipedia.org/wiki/Transitive_closure#In_logic_and_computational_complexity). So it can be expected that this would be a little tricky.
Negation is implemented in Prolog in a very rudimentary manner. You can essentially get a useful answer only if a negated query is sufficiently instantiated. To do this, define a relation person/1 that describes all persons you are considering. Then you can write:
gift(X,Y) :-
person(X),
person(Y),
\+ sameFamily(X,Y).
There is another issue with the definition of sameFamily/2.

How to find blood relatives only in prolog

Using recursion i need to find all blood relatives of any person in the family tree.
My attempt so far has failed.
Here is my code, with my attempt at the bottom
female(helen).
female(debbie).
female(louise).
female(yvonne).
female(belinda).
female(heather).
male(john).
male(andrew).
male(barry).
male(daniel).
male(charles).
parent(helen, debbie).
parent(helen, barry).
parent(helen, louise).
parent(john, debbie).
parent(john, barry).
parent(andrew, louise).
parent(debbie, yvonne).
parent(debbie, daniel).
parent(barry, charles).
parent(barry, belinda).
parent(louise, heather).
mother(X, Y) :-
female(X),
parent(X, Y).
father(X, Y) :-
male(X),
parent(X,Y).
child(X, Y) :-
parent(Y, X).
daughter(X, Y) :-
parent(Y, X),
female(X).
son(X, Y) :-
parent(Y,X),
male(X).
sister(X, Y) :-
female(X),
parent(Q,X),
parent(Q,Y).
brother(X, Y) :-
male(X),
parent(Q,X),
parent(Q,Y).
sibling(X, Y) :-
parent(Q,X),
parent(Q,Y),
X\=Y.
uncle(X, Y) :-
parent(P,Y),
brother(X,P).
aunt(X, Y) :-
parent(P,Y),
sister(X,P).
cousin(C, Cousin):-
parent(Parent,C),
sibling(Parent,AU),
child(Cousin,AU).
%Here is Relative
relative(An, Re):-
An\=Re,
parent(An, Re);
sibling(An, Re).
relative(An, Rela):-
parent(An, Child);
sibling(An, Rela),
relative(Child, Rela),
An\=Rela, C\=Rela.
Sort of works, but gets stuck in an infinite loop at the end.
Thanks.
not sure about 'relatives' (any person bound reachable in a parent/child relation ?), but your definition seems more complex than needed ( do you know what ; does ?).
I tried
relative(An, Re):-
parent(An, Re).
relative(An, Rela):-
parent(An, C),
relative(C, Rela).
that yields
16 ?- forall(relative(X,Y),writeln(X:Y)).
helen:debbie
helen:barry
helen:louise
john:debbie
john:barry
andrew:louise
debbie:yvonne
debbie:daniel
barry:charles
barry:belinda
louise:heather
helen:yvonne
helen:daniel
helen:charles
helen:belinda
helen:heather
john:yvonne
john:daniel
john:charles
john:belinda
andrew:heather
true.
edit I tried another relation, using a generalized parent/2, but still too permissive.
relative(Pers, Re):-
ancestor(Re, Pers) ; sibling(Pers, Re) ; cousin(Pers, Re) ; uncle(Re, Pers) ; aunt(Re, Pers).
ancestor(Anc, Pers) :- parent(Anc, Pers).
ancestor(Anc, Pers) :- parent(Anc, P), ancestor(P, Pers).
Maybe cousin/2 is too permissive also. Here is the graph
I guess that heather should have only luise,helen,andrew as relatives. It's this true ?
edit given latest comment, seems that the definition could be right. I get
24 ?- setln(X,relative(heather,X)).
andrew
barry
belinda
charles
daniel
debbie
helen
louise
yvonne
true.
that is everyone is related to heather apart john.
Here's one way that works, but it will sometimes produce duplicates. Using setof will give the unique collection. I avoided the miscellaneous relations and stuck with descendent or parent.
descendent(A, B) :-
parent(B, A).
descendent(A, B) :-
parent(C, A),
descendent(C, B).
relative(A, B) :-
descendent(B, A).
relative(A, B) :-
descendent(A, B).
relative(A, B) :-
descendent(A, C),
descendent(B, C),
A \= B.
setof(A, relative(heather, A), Relatives).
Relatives = [andrew,barry,belinda,charles,daniel,debbie,helen,louise,yvonne]
If you don't have setof, you can use the findall/3 and sort/2 ISO predicates:
findall(A, relative(heather, A), R), sort(R, Relatives).
Note that the solutions presented so far assume that all of the relatives have unique names. A general case of dealing with relatives with the same first name (and possibly the same last name) you would need to track and compare lineages for differences.

Match database items exactly once in Prolog?

Let's say there is a simple database of people in Prolog
person(john).
person(mary).
person(john).
person(susan).
I need to match the entires exactly once:
john-mary, john-john, john-susan, mary-john, mary-susan, john-susan
I tried coming up with something like this:
match:- person(X),!,person(Y), write(X),write(-), write(Y),nl.
run:- person(X), match(X), fail.
But it's matching many times, and matches a person to him/herself, which shouldn't be.
Basically, what I need is to iterate over all Xs and make Prolog to look strictly "below" for Ys.
A quick solution would be to number your people:
person(1, john).
person(2, mary).
person(3, john).
person(4, susan).
Then you could match people like this:
match(X-Y) :-
person(I, X), person(J, Y), I < J.
Since you have two john entries, I'm not sure any other solution is going to work. Normally you could fake an ordering using #>/2 but that would require your atoms to be unique, and since they aren't, it would prevent the john-john solution.
Edit: Since we're willing to use findall/3 to materialize the database of people, we can treat this as a list problem and find a functional solution. Let's get all the combinations in a list:
combinations([X|Rest], X, Y) :- member(Y, Rest).
combinations([_|Rest], X, Y) :- combinations(Rest, X, Y).
With this predicate in hand, we can find the solution:
combined_folks(People) :-
findall(P, person(P), Persons),
findall(X-Y, combinations(Persons, X, Y), People).
?- combined_folks(X).
X = [john-mary, john-john, john-susan, mary-john, mary-susan, john-susan].
That actually turned out to be pretty clean!
person(john).
person(mary).
person(john).
person(susan).
match :- findall(P,person(P),People), match_all(People).
match_all([_]) :- !.
match_all([P|People]) :- match_2(P,People), match_all(People).
match_2(_,[]) :- !.
match_2(P1,[P2|People]) :- format('~a-~a~n',[P1,P2]), match_2(P1,People).
?- match.

Resources