Nephew of my own father in prolog - prolog

i'm programming a family in prolog and i'm having trouble with the nephew implementation. When I ask if erick is the nephew of Alberto it returns true, when it should return false because Alberto is the father of erick, however, it does works for all the other cases that should be true. If someone could help me I would be very grateful.
My code:
man(beto).
man(fransisco).
man(alberto).
man(jaime).
man(manolo).
man(nolo).
man(lito).
man(manuel).
man(erick).
man(jesu).
man(jesus).
woman(emi).
woman(harumi).
woman(haru).
woman(yuneisi).
woman(yasmeli).
woman(mioara).
woman(elia).
woman(iza).
woman(alice).
woman(ofelia).
woman(arlet).
parent(manuel, alberto).
parent(ofelia, alberto).
parent(manuel, jaime).
parent(ofelia, jaime).
parent(manuel, manolo).
parent(ofelia, manolo).
parent(alberto, erick).
parent(alberto, beto).
parent(alberto, fransisco).
parent(emi, erick).
parent(emi, beto).
parent(manolo, nolo).
parent(manolo, arlet).
parent(nolo, lito).
parent(iza, lito).
parent(mioara, yuneisi).
parent(mioara, yasmeli).
parent(jaime, yuneisi).
parent(jaime, yasmeli).
parent(jesus_padre, jesu)
parent(jesus_padre, alice).
parent(jesus_padre, haru).
parent(harumi, haru).
parent(harumi, jesu).
parent(harumi, alice).
father(X,Y) :- parent(X,Y), man(X).
mother(X,Y) :- parent(X,Y), woman(X).
brother(X,Y) :- man(X), parent(F, X), parent(F, Y).
sister(X,Y) :- woman(X), parent(P, X), parent(P, Y).
grandpa(X,Y) :- father(F,Y), father(X,F), man(X).
grandma(X,Y) :- father(F,Y), mother(X,F), woman(X).
son(X,Y) :- father(Y,X), man(X).
nephew(X,Y) :- father(F,X), brother(F,Y).

Besides the missing dot after parent(jesus_padre, jesu) as pointed out by #LuaiGhunim there are a few other issues with your predicates. Your definition of brother/2 is too general. Nobody is his own brother but if you query your predicate you find several such instances:
?- brother(X,X).
X = beto ;
X = beto ;
X = fransisco ;
X = alberto ;
X = alberto ;
X = jaime ;
X = jaime ;
X = manolo ;
X = manolo ;
X = nolo ;
X = lito ;
X = lito ;
X = erick ;
X = erick ;
X = jesu ;
X = jesu ;
false.
You can easily remedy this by adding a goal dif/2:
brother(X,Y) :-
dif(X,Y),
man(X),
parent(F, X),
parent(F, Y).
Now the query above fails as it should:
?- brother(X,X).
false.
You'll still get a lot of pairs twice:
?- brother(X,Y).
X = beto, % <- 1st occurrence
Y = erick ; % <- 1st occurrence
X = beto,
Y = fransisco ;
X = beto, % <- 2nd occurrence
Y = erick ; % <- 2nd occurrence
.
.
.
The reason for that is that you can derive it via the mother or the father. In the above example (beto and erick) you'll get there via emi or alberto. These solutions might be redundant but they are correct. The same goes for your predicate sister/2:
?- sister(X,X).
X = haru ;
X = haru ;
X = yuneisi ;
X = yuneisi ;
X = yasmeli ;
X = yasmeli ;
X = alice ;
X = alice ;
X = arlet.
The remedy is the same as above:
sister(X,Y) :-
dif(X,Y),
woman(X),
parent(P, X),
parent(P, Y).
?- sister(X,X).
false.
?- sister(X,Y).
X = haru,
Y = jesu ;
X = haru,
Y = alice ;
X = haru,
Y = jesu ;
.
.
.
Your definition of grandma/2 and grandpa/2 on the other hand is too specific. To see this let's add the following facts to your code:
man(m1).
man(m2).
woman(w1).
woman(w2).
woman(w3).
parent(m1,w1).
parent(w1,w2).
parent(w2,w3).
Then the following queries should succeed but they fail instead:
?- grandpa(m1,w2).
false.
?- grandma(w1,w3).
false.
The reason for this is that the intermediate parent in your definition of grandpa/2 and grandma/2 is a father/2 where it should be a parent/2. Additionally the last goals (man(X) and woman(X)) are redundant since they are already covered by father/2 and mother/2 respectively. Instead, you could define the two predicates like so:
grandpa(X,Y) :-
parent(F,Y),
father(X,F).
grandma(X,Y) :-
parent(F,Y),
mother(X,F).
Now the above queries yield the desired result:
?- grandpa(m1,w2).
true.
?- grandma(w1,w3).
true.
Finally, a nephew according to the Cambridge Dictionary is a son of your sister or brother, or a son of the sister or brother of your husband or wife. Since you haven't got predicates for husbands and wives I'll stick with the a son of your sister or brother part. If you add facts for husbands and wives, you can add additional rules to cover the other part of the definition. You can write the first part of the definition in Prolog like so:
nephew(X,Y) :-
man(X),
dif(F,Y),
parent(P,F),
parent(P,Y),
parent(F,X).
If you query this predicate there's no erick/alberto solution any more:
?- nephew(erick,X).
X = jaime ;
X = manolo ;
X = jaime ;
X = manolo ;
false.

Prolog it's all about relations. Joins plays a fundamental role. So, often you can think in term of 'yields', or functions over the DB, and design/control the data access plan by means of joins (clauses, i.e. logical formulae) producing records - like SQL does. The procedural execution over the retrieved data is expressed in (almost) the same language: joins, giving us a taste of declarative programming. Anyway, here are the clauses provided by #tas in standard Prolog:
brother(X,Y) :-
man(X),
parent(F, X),
parent(F, Y),
X\=Y.
sister(X,Y) :-
woman(X),
parent(P, X),
parent(P, Y),
X\=Y.

Related

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

Prolog predicate issue

I'm having an issue with a part of my assignment. I'm supposed to write a predicate "friendly" which is supposed to be true when network member X is said to be friendly if X likes back everyone who likes him/her.
EDIT: In the below example, barry is friendly as the list of people that like barry is kara and barry likes kara back. Kara is NOT the right answer as the list of people that like Kara are Barry, Clark and Oliver BUT Kara only likes back Barry and Clark so Kara is not friendly.
eg of G = [person(kara, [barry, clark]),person(bruce, [clark, oliver]),person(barry, [kara, oliver]),person(clark, [oliver, kara]),person(oliver, [kara])]
What I have so far;
friendly(G, X):-
member_(person(X, _), G),
likers(G, X, L),
likes_all(G, X, L).
% to get the list of members who like X;
likers(G, X, [Y|T]) :-
likes(G, Y, X),
select_(Y, G, G2),
likers(G2, X, T).
likers([], _, []).
likers([], _, _).
likers(_, _, []).
% select is used to remove the person from the list once visited.
select_(X, [person(X, _)|T], T).
select_(X, [H|T], [H|R]) :-
select_(X, T, R).
% to check whether X likes all the list of people that like X;
likes_all(G, X, [H|T]):-
likes(G, X, H),
likes_all(G, X, T).
likes_all(_, _, []).
likes_all(G, [H|T], X):-
likes(G, H, X),
likes_all(G, T, X).
likes_all(_, [],_).
likes(G, X, Y):-
member_(person(X, L), G),
member_(Y, L).
member_(X, [X|_]).
member_(X, [_|T]) :-
member_(X, T).
My issue is it doesn't work properly. See sample output below.
So, I don't know what is wrong and how I'm supposed to do it. We are not allowed to use any built in predicates or control operators, so no !, ;, =, \=, +, etc, only pure prolog.
Any hint on moving forward is appreciated.
Output:
[debug] ?- friendly([person(kara, [barry, clark]),person(bruce, [clark, oliver]),person(barry, [kara, oliver]),person(clark, [oliver, kara]),person(oliver, [kara])], X).
X = kara ;
X = kara ;
X = kara ;
X = kara ;
X = kara ;
X = bruce ;
X = barry ;
X = barry ;
X = clark ;
X = clark ;
X = oliver ;
false.
I think my error is somewhere in likers function. Output of "likers":
?- likers([person(kara, [barry, clark]), person(bruce, [clark, oliver]), person(barry, [kara, oliver]), person(clark, [oliver, kara]), person(oliver, [kara])], kara, L).
L = [] ;
L = [barry] ;
L = [barry, clark] ;
L = [barry, clark, oliver] ;
L = [barry, oliver] ;
L = [barry, oliver, clark] ;
L = [clark] ;
L = [clark, barry] ;
L = [clark, barry, oliver] ;
L = [clark, oliver] ;
L = [clark, oliver, barry] ;
L = [oliver] ;
L = [oliver, barry] ;
L = [oliver, barry, clark] ;
L = [oliver, clark] ;
L = [oliver, clark, barry] ;
false.
In the above, the right answer would be L = [barry, clark, oliver] or one of the combinations. Is there a way to get that in pure prolog ?
After calming down, and a bit of google, here is an answer that ought to be satisfactory. The key idea is to pass around not only the person X we are interested in but also the complement set (AEX). Comparing against this is equivalent to negation of comparison with X.
friendly(X) :-
G = [person(kara,[barry,clark]),
person(bruce,[clark,oliver]),
person(barry,[kara,oliver]),
person(clark,[oliver,kara]),
person(oliver,[kara])],
allppl(G,All),
mymember(person(X,Xs),G),
select(X,All,AEX),
likers(G,X,AEX,[],Fs),
subset(Fs,Xs).
Get a list of all people:
allppl([person(P,_)|Rest],[P|Ps]) :-
allppl(Rest,Ps).
allppl([],[]).
This my old likers/4 plus the complement set.
likers([person(Y,Ys)|Rest],X,AEX,Fs0,Fs) :-
mymember(X,Ys),
likers(Rest,X,AEX,[Y|Fs0],Fs).
likers([person(_,Ys)|Rest],X,AEX,Fs0,Fs) :-
subset(Ys,AEX),
likers(Rest,X,AEX,Fs0,Fs).
likers([],_,_,Fs,Fs).
And here are some helper predicates.
select(X,[X|Xs],Xs).
select(X,[Y|Xs],[Y|Zs]) :-
select(X,Xs,Zs).
subset([],_).
subset([X|Xs],Ys) :-
mymember(X,Ys), subset(Xs,Ys).
mymember(X,[X|_]).
mymember(X,[_|Xs]) :-
mymember(X,Xs).
Now I get
?- friendly(X).
X = bruce ? ;
X = barry ? ;
no
Here's how your program should probably be written:
person(kara,[barry, clark]).
person(bruce,[clark,oliver]).
person(barry,[kara,oliver]).
person(clark,[oliver,kara]).
person(oliver,[kara]).
likes_back([],_).
likes_back([Y|Ys],X) :-
person(Y,Xs),
member(X,Xs),
likes_back(Ys,X).
friendly(X) :-
person(X,Ys),
likes_back(Ys,X).
?- friendly(X),write(X),nl,fail.
When I run it I only get kara as a result - which is correct based on my inspection of the data and rules you gave.
Ok, here is a solution then, if I understand correctly. I assume that people that nobody likes (Bruce) are not considered friendly.
friendly(P) :-
person_likes(P,Ps),
findall(X,(person_likes(X,Zs),member(P,Zs)),Fs), Fs \= [],
check_subset(Fs,Ps).
check_subset([X|Xs],Set) :-
memberchk(X,Set),
check_subset(Xs,Set).
check_subset([],_).
person_likes(kara,[barry,clark]).
person_likes(bruce,[clark,oliver]).
person_likes(barry,[kara,oliver]).
person_likes(clark,[oliver,kara]).
person_likes(oliver,[kara]).
Result:
?- friendly(X).
X = barry ? ;
no
If you are not allowed to use findall/3 then you need to write another three lines of code or so yourself.
You could write likers/4 like this:
likers([person(Y,Ys)|Rest],X,Fs0,Fs) :-
( memberchk(X,Ys) ->
Fs1 = [Y|Fs0]
; Fs1 = Fs0 ),
likers(Rest,X,Fs1,Fs).
likers([],_,Fs,Fs).
Call it with third parameter empty list.
Addition: without semicolon:
likers([person(Y,Ys)|Rest],X,Fs0,Fs) :-
memberchk(X,Ys),
likers(Rest,X,[Y|Fs0],Fs).
likers([person(_,Ys)|Rest],X,Fs0,Fs) :-
\+ memberchk(X,Ys),
likers(Rest,X,Fs0,Fs).
likers([],_,Fs,Fs).

Prolog, about how to form better clauses

I have following clauses:
num_parent(adam, X) :- !, X = 0.
num_parent(eve, X) :- !, X = 0.
num_parent(X, 2).
When I typed the query:
num_parent(eve,X).
It only returns:
X = 0.
which is what I want.
But when I typed this query:
num_parent(X,0).
it only returns:
X = adam.
So how can I modify the clauses to make it return:
X = adam;
X = eve.
Thanks
First, try to formulate what you want in plain English. You probably want to say:
Everyone has two parents except Adam and Eve who have none.
What about Lilith? Never mind, let's stick to your reading.
num_parent(Person, 2) :-
dif(Person, adam),
dif(Person, eve).
num_parent(adam, 0).
num_parent(eve, 0).
As you can see, it is a bit cumbersome to define this: You have to mention each exceptional person twice. Easy to make an error.
With if_/3 available in library(reif)
for
SICStus and
SWI
you can write more succinctly:
num_parent(Person, Num) :-
if_( ( Person = adam ; Person = eve ), Num = 0, Num = 2 ).
And now some uses:
?- num_parent(eve, Num).
Num = 0.
?- num_parent(X, 0).
X = adam
; X = eve
; false.
?- num_parent(X, 2).
dif(X, eve), dif(X, adam).
?- num_parent(lilith, 2).
true.
The programs returns only X = adam because you inserted the cut !. The cut is used when you have found the right rules and you don't need to do further evaluations, but it cuts all the other solutions.
In your case
num_parent(adam, X) :- !, X = 0.
num_parent(eve, X) :- !, X = 0.
num_parent(_, 2). %replaced num_parent(X, 2) with num_parent(_, 2) to avoid singleton variable
num_parent(X, 0) returns only X = adam.
If you write
num_parent(adam, X) :- X = 0.
num_parent(eve, X) :- !, X = 0.
num_parent(_, 2).
the solution will be X = adam and X = eve, and in this case:
num_parent(adam, X) :- X = 0.
num_parent(eve, X) :- X = 0.
num_parent(_, 2).
the solution will be X = adam, X = eve and false because the query num_parent(X, 0) doesn't unify with num_parent(_, 2).
You can better see this behavior using the tracer.

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.

Prolog: Unification or Backtracking errors in program

I have a simple knowledge base that encodes a family tree. Some important rules in this representation are as follows:
% fathers
father(michael,cathy).
father(michael,sharon).
father(charles_gordon,michael).
father(charles_gordon,julie).
father(charles,charles_gordon).
father(jim,melody).
father(jim,crystal).
father(elmo,jim).
father(greg,stephanie).
father(greg,danielle).
% mothers
mother(melody,cathy).
mother(melody,sharon).
mother(hazel,michael).
mother(hazel,julie).
mother(eleanor,melody).
mother(eleanor,crystal).
mother(crystal,stephanie).
mother(crystal,danielle).
% parents
parent(X,Y) :- father(X,Y).
parent(X,Y) :- mother(X,Y).
% men
male(michael).
male(charles_gordon).
male(charles).
male(jim).
male(elmo).
male(greg).
% women
female(cathy).
female(sharon).
female(julie).
female(hazel).
female(eleanor).
female(melody).
female(crystal).
female(stephanie).
female(danielle).
person(X) :- male(X) ; female(X).
parent(X,Y) :- father(X,Y) ; mother(X,Y). % X is parent of Y
child(X,Y) :- parent(Y,X).
elder(X,Y) :- parent(X,Y). % X is an elder of Y, meaning X is a parent or an ancestor of Y
elder(X,Y) :- parent(X,Z), elder(Z,Y).
junior(X,Y) :- child(X,Y). % X is a junior of Y, meaning X is a child or some descendant of Y
junior(X,Y) :- child(X,Z), junior(Z,Y).
I am attempting to find the nearest elder between two individuals(predicate ne(X,Y,Z)). This individual Z is the elder of both X and Y, and no junior of Z is also an elder of BOTH X and Y.
My attempt looks like this:
ne(X,Y,Z) :- person(X),
person(Y),
X \= Y,
elder(Z,X),
elder(Z,Y),
junior(A,Z),
not(elder(A,X)),
not(elder(A,Y)).
but this is somehow incorrect, because whenever I run ?- ne(stephanie,cathy,Z). i get
Z = jim ;
Z = jim ;
Z = jim ;
Z = jim ;
Z = elmo ;
Z = elmo ;
Z = elmo ;
Z = elmo ;
Z = eleanor ;
Z = eleanor ;
Z = eleanor ;
Z = eleanor ;
but i'm only supposed to get one answer, and I can't figure out what's wrong. Thanks!
from this graph
seems this answer is correct
?- ne(stephanie,cathy,A).
A = eleanor ;
A = jim.
here is my attempt to ne/3
ne(X,Y,Z) :-
setof(A, (
elder(A, X),
elder(A, Y),
X \= Y,
\+ (elder(A, T), elder(T, X) , elder(T, Y) ))
, As), member(Z, As).
not sure it's the better way...
Setof/3 (joined with member/2) is used to eliminate duplicate answers, since we get
?- aggregate(count,A^ne(stephanie,cathy,A),N).
N = 32.
with this core logic
ne(X,Y,A) :-
elder(A, X),
elder(A, Y),
X \= Y,
\+ (elder(A, T), elder(T, X) , elder(T, Y)).
note variable A replaces locally the original Z
edit
I didn't took in account the savvy comment by #Boris, but after removing the duplicate parent/2 definition, the setof/3+member/2 trick become useless.

Resources