Prolog bidirectional predicate - prolog

I have to create family relations in Prolog for an assignment and I stumbled upon this problem.
man(john).
woman(lisa).
married(john,lisa).
?- married(john,X).
X = lisa.
?- married(X,john).
false.
How to make this predicate work in two ways?
If john is married to lisa, then lisa is married to john.
For facts I can only use gender, parent, and married:
man(john).
woman(lisa).
parent(john,steve).
parent(lisa,steve).
married(john,lisa).
That requirement makes this solution unusable for me. I can't just add relation wife(lisa,john). because I have to define wife, husband etc. by myself, like.
wife(X,Y) :- woman(X),married(X,Y).

Instead of trying to make married/2 bidirectional, you could define your predicate wife/2 accordingly. If the first argument of married/2 is always the husband and the second always the wife, just flip the arguments of the second goal:
wife(X,Y) :-
woman(X),
married(Y,X). % <- changed to Y,X instead of X,Y
With your given facts this yields the desired result:
?- wife(X,Y).
X = lisa,
Y = john.
If your facts also include pairs the other way around, e.g.:
woman(ann).
man(anton).
married(ann, anton).
you can include a second rule (namely your original rule) for wife/2 to deal with such pairs as well:
wife(X,Y) :-
woman(X),
married(Y,X).
wife(X,Y) :- % <- new rule
woman(X),
married(X,Y).
With the additional rule and facts the above query now yields:
?- wife(X,Y).
X = lisa,
Y = john ;
X = ann,
Y = anton.

If you can't add helper relation (it sounds like homework exercise), try to use predicate #< which operates on the "Standard Order of Terms":
married(A,B) :- A #< B, married(B,A).
Note, however, that this solution brings also some negative aspects (please read this answer).

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].

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.

How to define `not_a_parent()` predicate?

How to create such logic in GNU Prolog? How to define not_a_parent() predicate?
parent(john,chris).
parent(mary,chris).
not_a_parent(X) :- \+ parent(X,Y).
The interesting answer to the similar question is What is the logical 'not' in Prolog?. But I do not see how to implement it here.
This worked for me:
parent(john,chris).
parent(mary,chris).
parent(mary,suzanne).
parent(suzanne,jane).
parent(suzanne,peter).
parent(peter,rose).
parent(jerry,rose).
parent(jane,carl).
not_a_parent(NonParents) :- setof(Z,Y^parent(Y,Z),SetOfChildren),
findNonParents(SetOfChildren, NonParents, []),!.
findNonParents([],A,A).
findNonParents([H|SetOfChildren], NonParents, A):-
not(call(parent(H,_))),
findNonParents(SetOfChildren,NonParents,[H|A]).
findNonParents([_|SetOfChildren], NonParents, A):-
findNonParents(SetOfChildren,NonParents,A).
Result for querying not_a_parent(NonParents) is:
?- not_a_parent(NonParents).
NonParents = [rose, chris, carl].
You need to enumerate all the persons somehow. For example
not_a_parent(X) :- ( X = john ; X = mary ; X = chris ), \+ parent(X,_).
In any real program you would probably have some simple way to get all persons, and then you can do
not_a_parent(X) :- person(X), \+ parent(X,_).
You need to get all the instances of parent/2 such that X never unifies parent(X,_). findall(Template, Goal, Instances) executes the Goal until it fails and populate the list Instances with the terms that unify the Template. That way, if the list Instances is empty then parent(X,_) does not exist.
So your predicate would be like:
not_a_parent(X):- findall(_, parent(X,_) , []).

Proper way to Declare and Search List in Prolog?

I'm new to Prolog. Is it possible to "declare" a list as a fact in Prolog, and then access it easily? I'm working on a family-tree type problem. To start, I would like to declare a bunch of males as belonging to a list of males. I then would like to be able to query if a particular person is a male. Here is the code I've written so far:
% ------------------------------------------------------------------------- %
% Facts:
males([john, joseph, aaron, peter, paul, mark, ben, adam, daniel]).
% ------------------------------------------------------------------------- %
% Rules:
% Member of a List
member(X, [X|_]).
member(X, [_|T]) :- member(X, T).
male(X) :- member(X, males).
However, my output is not as expected:
21 ?- male(john).
false.
I know my member() rules are working, since I can produce the following result:
23 ?- member(john, [peter, paul, john]).
true
Can anyone point me in the right direction? Thank you in advance.
The usual proper way to do this in Prolog would be to declare your list as a table of facts:
male(john).
male(joseph).
% etc
If you do it like this, you have already defined your male/1 and can query it either like this:
?- male(joseph).
true.
Or like this:
?- male(M).
M = john ;
M = joseph ;
% etc
You can start with Prolog by reading through the first chapter of "Learn Prolog Now!". Its first example is exactly the same as your problem.
I'm not an expert in prolog but I know a solution to this (could be not the only one)
male(X) :- males(Y), member(X, Y)
The way I understand it is that predicates doesn't return variables, they assign values to the parameters given, so Y now have been assigned the array of males and now you can look for X in Y

Resources