Creating a Niece Rule in Prolog - 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.

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

Nephew of my own father in 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.

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.

Adding a predicate based on the relationship between people

Add the predicate related(X,Y) such that x is related to y if x and y have any ancestor in common but are not the same person
For my homework i need to add the predicate to the .PL i have to prove if 2 people are related. i have worked it out so it will say if they are related if they are brother and sister but i just cant figure out the code for cousins and so on. any help would be much appreciated.
% File FAMILY.PL% Part of a family tree expressed in Prolog
% In father/2, mother/2, and parent/2,
% first arg. is parent and second arg. is child.
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).
mother(melody,cathy).
mother(melody,sharon).
mother(hazel,michael).
mother(hazel,julie).
mother(eleanor,melody).
mother(eleanor,crystal).
mother(crystal,stephanie).
mother(crystal,danielle).
parent(X,Y) :- father(X,Y).
parent(X,Y) :- mother(X,Y).
sibling(X,Y) :- mother(M,X), mother(M,Y), \+ X == Y.
ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y).
related(X,Y) :- sibling(X,Y), \+ X == Y.
I was trying to add something like this but no luck.
related(X,Y) :- ancestor(Z,X),ancestor(Z,Y).
so i added
related(X,Y) :- parent(Z,X),parent(P,Y),parent(Q,Z),parent(Q,P), \+ X == Y.
and that is working in all my tests. anything wrong with that? or a better way to write it?
Among other problems, your ancestor rule cannot complete, being a recursive rule and missing the base case. Try
ancestor(X,Y) :- parent(X,Y). % base case
ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y). % recursive case
Note: I'm unsure about the linguistic sense of sibling/2, but should be independent of gender, I think
sibling(X,Y) :- parent(P,X), parent(P,Y), X \= Y.
And related/2 could be
related(X,Y) :- ancestor(A,X), ancestor(A,Y), X \= Y.
Complementary to ancestor = The next generation …
nextgen(X,Y) :- parent(Y,X).
nextgen(X,Y) :- parent(Y,Z), nextgen(Z,X).
In order to propose something more refined than ancestor/2 which is talked about tons of times, you can also add depth with something like ancestor/3 :
ancestor(X,Y,0) :-
% Base : X ancestor of Y and N=0 if
parent(X,Y). % X parent of Y
ancestor(X,Y,N) :-
% Recursive : X ancestor of Y if
N>0,
parent(X,Z), % X parent of Z and
M is N-1,
ancestor(Z,Y,M). % Z also ancestor of Y

Don't repeat solutions in Prolog

Suppose you have a database with the following content:
son(a, d).
son(b, d).
son(a, c).
son(b, c).
So a and b are sons of d and c. Now you want to know, given a bigger database, who is brother to who. A solution would be:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y.
The problem with this is that if you ask "brother(X, Y)." and start pressing ";" you'll get redundant results like:
X = a, Y = b;
X = b, Y = a;
X = a, Y = b;
X = b, Y = a;
I can understand why I get these results but I am looking for a way to fix this. What can I do?
Prolog will always try to find every possible solution available for your statements considering your set of truths. The expansion works as depth-first search:
son(a, d).
son(b, d).
son(a, c).
son(b, c).
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y.
brother(X, Y)
_______________________|____________________________ [son(X, P)]
| | | |
X = a, P = d X = b, P = d X = a, P = c X = a, P = b
| | | |
| ... ... ...
|
| (X and P are already defined for this branch;
| the algorithm now looks for Y's)
|__________________________________________ [son(Y, d)]
| |
son(a, d) -> Y = a son(b, d) -> Y = b
| |
| | [X \= Y]
X = a, Y = a -> false X = a, Y = b -> true
|
|
solution(X = a, Y = b, P = d)
But, as you can see, the expansion will be performed in all the branches, so you'll end up with more of the same solution as the final answer. As pointed by #Daniel Lyons, you may use the setof built-in.
You may also use the ! -- cut operator -- that stops the "horizontal" expansion, once a branch has been found to be valid, or add some statement that avoids the multiple solutions.
For further information, take a look at the Unification algorithm.
First, I would advise against updating the Prolog database dynamically. For some reasons, consider the article
"How to deal with the Prolog dynamic database?".
You could use a combination of the builtin setof/3 and member/2, as #DanielLyons has suggested in his answer.
As yet another alternative, consider the following query which uses setof/3 in a rather unusual way, like this:
?- setof(t,brother(X,Y),_).
X = a, Y = b ;
X = b, Y = a.
You can eliminate one set with a comparison:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y, X #< Y.
?- brother(X, Y).
X = a,
Y = b ;
X = a,
Y = b ;
false.
Since X and Y will be instantiated both ways, requiring X be less than Y is a good way to cut the solutions in half.
Your second problem is that X and Y are brothers by more than one parent. The easiest solution here would be to make your rules more explicit:
mother(a, d).
mother(b, d).
father(a, c).
father(b, c).
brother(X, Y) :-
mother(X, M), mother(Y, M),
father(X, F), father(Y, F),
X \= Y, X #< Y.
?- brother(X, Y).
X = a,
Y = b ;
false.
This method is very specific to this particular problem, but the underlying reasoning is not: you had two copies because a and b are "brothers" by c and also by d—Prolog was right to produce that solution twice because there was a hidden variable being instantiated to two different values.
A more elegant solution would probably be to use setof/3 to get the solutions. This can work even with your original code:
?- setof(X-Y, (brother(X, Y), X #< Y), Brothers).
Brothers = [a-b].
The downside to this approach is that you wind up with a list rather than Prolog generating different solutions, though you can recover that behavior with member/2.
This should work. But I think it can be improved (I am not a Prolog specialist):
brother(X, Y) :-
son(X, P1),
son(Y, P1),
X #< Y,
(son(X, P2), son(Y, P2), P1 #< P2 -> false; true).
If you're using Strawberry Prolog compiler,you won't get all the answers by typing this:
?- brother(X, Y),
write(X), nl,
write(Y), nl.
In order to get all the answers write this:
?- brother(X, Y),
write(X), nl,
write(Y), nl,
fail.
I hope it helps you.:)
I got to an answer.
% Include the dictionary
:- [p1]. % The dictionary with sons
:- dynamic(found/2).
brother(X, Y) :-
% Get two persons from the database to test
son(X, P),
son(Y, P),
% Test if the two persons are different and were not already used
testBrother(X, Y).
% If it got here it's because there is no one else to test above, so just fail and retract all
brother(_, _) :-
retract(found(_, _)),
fail.
testBrother(X, Y) :-
X \= Y,
\+found(X, Y),
\+found(Y, X),
% If they were not used succed and assert what was found
assert(found(X, Y)).
It always returns fails in the end but it succeeds with the following.
brother(X, Y). % Every brother without repetition
brother('Urraca', X). % Every brother of Urraca without repetition
brother('Urraca', 'Sancho I'). % True, because Urraca and Sancho I have the same father and mother. In fact, even if they only had the same mother or the same father it would return true. A little off context but still valid, if they have three or more common parents it would still work
It fails with the following:
brother(X, X). % False because it's the same person
brother('Nope', X). % False because not is not even in the database
brother('Nope', 'Sancho I'). % False, same reason
So like this I can, for example, ask: brother(X, Y), and start pressing ";" to see every brother and sister without any repetition.
I can also do brother(a, b) and brother(b, a), assuming a and b are persons in the database. This is important because some solutions would use #< to test things and like so brother(b, a) would fail.
So there it is.

Resources