Prolog - Chain rule for Step Siblings - prolog

I am a newbie to Prolog, and have a question regarding programming a "chain rule" for step-siblings that share a "common parent".
In my program, I am assuming that the existence of the parent(X,Y) fact that asserts that X is a parent of Y.
I need a rule chain(X,Y,L): if X is an ancestor of Y, then L is the list containing X, Y and all ancestors of Y who are also descendants of X, listed in descending order of age (oldest first). In other words, my list should contain all the people that link a person with an ancestor.
Eg: If chain(peter,mary,[peter,paul,sue,mary]), then peter is the parent of paul, paul is the parent of sue, and sue is the parent of mary.
Note: I am familiar with the stepSibling(a,b) relationship where their relationship is qualified via their parents partner(X,Y); where siblings a and b are children of their respective parents via the relationship child(a,X) and child(b,Y). Hence; I am only confused with the relationship where both stepsiblings share a common parent. ie. A child relationship that may look like this: child(a,X) and child(b,X).

This is kind of an interesting twist on the usual genealogy problems we discuss in early Prolog courses. Let's consider a simplification first, ancestor(X,Y) is true if X is an ancestor of Y.
So you have a predicate parent(X,Y) which says X is a parent of Y. You can write ancestor/2 like this:
% base case: your parent is an ancestor
ancestor(X, Y) :- parent(X, Y).
ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).
With a sample database like this it should work fine:
parent(peter, paul).
parent(paul, sue).
parent(sue, mary).
This will work for a query like ancestor(peter, mary), which is close to what you want. The next step would be to retain the chain, which would look something like this:
chain(X, Y, [X,Y]) :- parent(X, Y).
chain(X, Z, [X|Chain]) :- parent(X, Y), chain(Y, Z, Chain).
This appears to work:
?- chain(peter, mary, X).
X = [peter, paul, sue, mary] ;
false.
I worry though, because your question mentions step siblings and that the chain should include other people. Is that just chaff or do you have additional requirements? If so, they're not reflected in your example, so I may need you to augment your question with additional details.

Related

PROLOG family tree: how can I output all the siblings and cousins?

Hi I am working on a PROLOG family tree question, and this is what I have so far:
/*1. Write Prolog clauses to express the following three relationships,
* given the parent/2 relationship: grandparent/2, sibling/2, cousin/2.*/
% clauses
parent(jill, amy).
parent(jill, tim).
parent(jill, john).
parent(amy, grace).
parent(amy, anna).
parent(tim, sam).
parent(tim, joel).
parent(tim, ben).
% rules
grandparent(X,Y) :-
parent(Z,Y),
parent(X,Z).
sibling(X, Y) :-
parent(Z, X),
parent(Z, Y).
cousin(X,Y) :-
parent(P, X),
parent(S, Y),
sibling(P, S).
When I put:
?- sibling(X, tim).
the output gives:
X = amy
but both john and amy are tim's sibling. The same problem happens with:
?- cousin(ben, X).
which gives:
X = grace
when both grace and anna are ben's cousins.
What changes do I need to make in order for the code to output all of tim's siblings and ben's cousins?
Thanks. :)
First of all, you've got a little bug over there.
You should correct the sibling rule - just a small hint here, try to use the rule as so
sibling(grace,grace)
and back to your issue, after you're getting first response click the ; or any of these ; n r space TAB keys, as the result you see is the first correct response. If you want to see the next correct result you need to use one of the keys above.
You can also try to use findall predicate to see all the results in the list
?- findall(X, cousin(grace, X),Z).
Z = [sam, joel, ben].

How to model the "sister" relationship so that it implies the gender

How can you define mother/father/sister/brother relationships such that asserting sister(bart, maggie) makes ?- female(maggie) evaluate as true?
I'm working on a toy family relation problem. This is what I have:
parent(bart, homer).
parent(bart, marge).
parent(lisa, homer).
parent(lisa, marge).
male(homer).
male(bart).
female(marge).
female(lisa).
mother(X,Y) :- female(Y), parent(X,Y).
father(X,Y) :- male(Y), parent(X,Y).
sister(X,Y) :- female(Y), father(X,F), father(Y,F), mother(X,M), mother(Y,M), X/=Y.
I'd like to be able to do the following:
sister(bart, maggie).
?- female(maggie).
% expect yes
i.e. support the "common sense" definition where if you assert that maggie is bart's sister, we know that maggie is female, while still obtaining sister(bart, lisa) being true based on the parent/gender assertions I already have.
As it is, your system expects male/female and parent to be a fact and derives father/mother/brother/sister through rules
You could do this:
% X is female if it is somebody's sister.
female(X) :- sister(_,X).
But combined with the other rule, it probably won't terminate (X is female if she is sb's sister, she is sb.'s sister if she is female...)
You could set different facts and rules to reverse this way of doing it, e.g. decide that basic facts are sister, mother, son, daughter... But you need to decide what forms elementary information in your database, and what forms derived information. If you want anything to be derived from anything, while staying with simple rules, you quickly end up with a system that doesn't terminate, exploring more and more ways to derive information that is already known.
If you want to have a richer set of inference rules (e.g. if X implies Y and Y is false, then X is false), and make sure your process terminates, doesn't give you the result multiple times, etc, you need to write (or borrow) your own rule interpreter, that is, your own inference engine.
Common sense reasoning is in general way too complex to implement for any general purpose programming language. For this very limited domain, we can prototype in SWI-Prolog a simple minded metainterpreter that generalize the goal selection from rules body, and avoids loops by means of tabling:
:- autoload(library(tabling)).
:- table solve/1, rule/2.
solve(F) :-
rule(F,[]).
solve(H) :-
rule(H,B),
solve_b(B).
% allowed builtins
solve(X\=Y) :- X\=Y.
solve_b([]).
solve_b(B) :-
select(G,B,R),
solve(G),
solve_b(R).
rule(parent(bart, homer), []).
rule(parent(bart, marge), []).
rule(parent(lisa, homer), []).
rule(parent(lisa, marge), []).
rule(male(homer), []).
rule(male(bart), []).
rule(male(M), [father(_,M)]).
rule(male(M), [brother(_,M)]).
rule(female(marge), []).
rule(female(lisa), []).
rule(female(F), [mother(_,F)]).
rule(female(F), [sister(_,F)]).
rule(mother(X,Y), [female(Y), parent(X,Y)]).
rule(father(X,Y), [male(Y), parent(X,Y)]).
rule(sister(X,Y), [
female(Y),
sibling(X,Y)
]).
rule(brother(X,Y), [
male(Y),
sibling(X,Y)
]).
rule(sibling(X,Y), [
father(X,F),
father(Y,F),
mother(X,M),
mother(Y,M),
X\=Y
]).
rule(sister(bart,maggie), []).

Sibling relation that just doesn't seem to cover all cases

I've been tasked with creating a sister relation in prolog, among other basic relations. The issue is that my assertions dont cover all angles for reasons that I just dont get. Currently I have
female(X)).
male(X).
parent(X,Y).
sibling(X,Y) :- female(X), parent(Z,X), parent(Z,Y).
female(mom).
female(mary).
male(tim).
parent(mom,tim).
parent(mom,mary).
With that, my code works fine when testing it with something like sister(mary,tim) (equals true) or sister(father,tim) (equals false) but i'm currently having issues with it defining sister(mom,tim) as true. While that may very well be a true statement somewhere on this world of ours, it's not something I feel is correct given the assignment im working on.
Do not start your programs with things like this:
female(X)).
male(X).
parent(X,Y).
You may think that these are "declarations" of the relations you will be using, but they are not. They are definitions of rules saying "anyone is female", "anyone is male", and "any object whatsoever is a parent of any object whatsoever". Delete these.
Then, let's decompose your problem a bit. A sister is a female sibling. The sibling relation is useful in itself, so let's define that first without worrying about sisters in particular:
siblings(X, Y) :-
parent_of(Parent, X),
parent_of(Parent, Y).
parent_of(mom, tim).
parent_of(mom, mary).
Observe how I renamed your parent relation to parent_of. This is not a symmetric relation, and for a term like parent(X, Y) we might not know which argument is the parent and which one is the child. Naming it parent_of is more suggestive: parent_of(X, Y) means (reading left to right): X is the parent of Y.
We can now test this:
?- siblings(X, Y).
X = Y, Y = tim ;
X = tim,
Y = mary ;
X = mary,
Y = tim ;
X = Y, Y = mary.
Note that this is not correct yet! It says that Tim is his own sibling, and that Mary is her own sibling. You need to fix that. I'll keep using it for the moment.
Now, as we said, a sister is a female sibling. That's easy to express now:
sister_of(Sister, Sibling) :-
female(Sister),
siblings(Sister, Sibling).
female(mom).
female(mary).
male(tim).
Sister is the sister of some Sibling if Sister is female and they are siblings. That is all. Note that these variable names are more informative than X and Y!
Let's test:
?- sister_of(Sister, Sibling).
Sister = mary,
Sibling = tim ;
Sister = Sibling, Sibling = mary.
Only Mary is anybody's sister, which is what we want. She is also her own sister, which we do not want, but that is the same problem noted above in the definition of siblings/2.

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.

Mutually referential Prolog rules in family tree

I've seen various implementations of family trees in Prolog, but I haven't found one that does what I would like to do, which is to define child and parent by referring to each other.
I want to do this because sometimes I have a fact that someone is a child of someone, at other times I have the fact that someone is the parent of someone. From either of these kinds of facts, I'd like to be able to ask who are parents and who are children.
My attempt at coding this is :-
parent(mary, fred).
child(john, peter).
child(paul, peter).
parent(P, C).
parent(P, C) :- child(C, P).
child (C, P).
child(C, P) :- parent(P, C).
This seems to work ok except that it will continue to give me the same results duplicated over and over. Eg :-
[3] ?- parent(peter, X).
true ;
X = john ;
X = paul ;
true ;
X = john ;
X = paul ;
true
Is there a way I can get it to stop after it has given me the full set of results once ?
More generally, is this kind of definition a weird thing to want to do (because of the mutual recursion) ? I want to be able to have facts of either parent or child, as reported, but also to infer the "opposite" relationship from these.
Thanks !
The issue with your program is that you are merging predicates.
parent/2 and child/2 are facts, you should not name a rule like a fact that is already defined in your program.
Rename the rules and all will work. Also, the base clause in your rules should add a condition, that match the fact, something like this:
parent(mary, fred).
child(john, peter).
child(paul, peter).
isparent(P, C):- parent(P, C).
isparent(P, C):- child(C, P).
ischild(C, P):- child(C, P).
ischild(C, P) :- parent(P, C).
Now the query:
?- isparent(peter, X).
X = john
X = paul
Also, don't use the complement rule in your condition, that's not necessary and will avoid you the recursion

Resources