Mutually referential Prolog rules in family tree - prolog

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

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

I have defined multiple predicates that seem to share a common form

All of these predicates are defined in pretty much the same way. The base case is defined for the empty list. For non-empty lists we unify in the head of the clause when a certain predicate holds, but do not unify if that predicate does not hold. These predicates look too similar for me to think it is a coincidence. Is there a name for this, or a defined abstraction?
intersect([],_,[]).
intersect(_,[],[]).
intersect([X|Xs],Ys,[X|Acc]) :-
member(X,Ys),
intersect(Xs,Ys,Acc).
intersect([X|Xs],Ys,Acc) :-
\+ member(X,Ys),
intersect(Xs,Ys,Acc).
without_duplicates([],[]).
without_duplicates([X|Xs],[X|Acc]) :-
\+ member(X,Acc),
without_duplicates(Xs,Acc).
without_duplicates([X|Xs],Acc) :-
member(X,Acc),
without_duplicates(Xs,Acc).
difference([],_,[]).
difference([X|Xs],Ys,[X|Acc]) :-
\+ member(X,Ys),
difference(Xs,Ys,Acc).
difference([X|Xs],Ys,Acc) :-
member(X,Ys),
difference(Xs,Ys,Acc).
delete(_,[],[]).
delete(E,[X|Xs],[X|Ans]) :-
E \= X,
delete(E,Xs,Ans).
delete(E,[X|Xs],Ans) :-
E = X,
delete(E,Xs,Ans).
There is an abstraction for "keep elements in list for which condition holds".
The names are inclide, exclude. There is a library for those in SWI-Prolog that you can use or copy. Your predicates intersect/3, difference/3, and delete/3 would look like this:
:- use_module(library(apply)).
intersect(L1, L2, L) :-
include(member_in(L1), L2, L).
difference(L1, L2, L) :-
exclude(member_in(L2), L1, L).
member_in(List, Member) :-
memberchk(Member, List).
delete(E, L1, L) :-
exclude(=(E), L1, L).
But please take a look at the implementation of include/3 and exclude/3, here:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/apply.pl?show=src#include/3
Also in SWI-Prolog, in another library, there are versions of those predicates called intersection/3, subtract/3, delete/3:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#intersection/3
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#subtract/3
https://www.swi-prolog.org/pldoc/doc_for?object=delete/3
Those are similar in spirit to your solutions.
Your next predicate, without_duplicates, cannot be re-written like that with include/3 or exclude/3. Your implementation doesn't work, either. Try even something easy, like:
?- without_duplicates([a,b], L).
What happens?
But yeah, it is not the same as the others. To implement it correctly, depending on whether you need the original order or not.
If you don't need to keep the initial order, you can simply sort; this removes duplicates. Like this:
?- sort(List_with_duplicates, No_duplicates).
If you want to keep the original order, you need to pass the accumulated list to the recursive call.
without_duplicates([], []).
without_duplicates([H|T], [H|Result]) :-
without_duplicates_1(T, [H], Result).
without_duplicates_1([], _, []).
without_duplicates_1([H|T], Seen0, Result) :-
( memberchk(H, Seen0)
-> Seen = Seen0 , Result = Result0
; Seen = [H|Seen0], Result = [H|Result0]
),
without_duplicates_1(T, Seen, Result0).
You could get rid of one argument if you use a DCG:
without_duplicates([], []).
without_duplicates([H|T], [H|No_duplicates]) :-
phrase(no_dups(T, [H]), No_duplicates).
no_dups([], _) --> [].
no_dups([H|T], Seen) -->
{ memberchk(H, Seen) },
!,
no_dups(T, Seen).
no_dups([H|T], Seen) -->
[H],
no_dups(T, [H|Seen]).
Well, these are the "while loops" of Prolog on the one hand, and the inductive definitions of mathematical logic on the other hand (See also: Logic Programming, Functional Programming, and Inductive Definitions, Lawrence C. Paulson, Andrew W. Smith, 2001), so it's not surprising to find them multiple times in a program - syntactically similar, with slight deviations.
In this case, you just have a binary decision - whether something is the case or not - and you "branch" (or rather, decide to not fail the body and press on with the selected clause) on that. The "guard" (the test which supplements the head unification), in this case member(X,Ys) or \+ member(X,Ys) is a binary decision (it also is exhaustive, i.e. covers the whole space of possible X)
intersect([X|Xs],Ys,[X|Acc]) :- % if the head could unify with the goal
member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
intersect([X|Xs],Ys,Acc) :- % if the head could unify with the goal
\+ member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
Other applications may need the equivalent of a multiple-decision switch statement here, and so N>2 clauses may have to be written instead of 2.
foo(X) :-
member(X,Set1),
(...action...).
foo(X) :-
member(X,Set2),
(...action...).
foo(X) :-
member(X,Set3),
(...action...).
% inefficient pseudocode for the case where Set1, Set2, Set3
% do not cover the whole range of X. Such a predicate may or
% may not be necessary; the default behaviour would be "failure"
% of foo/1 if this clause does not exist:
foo(X) :-
\+ (member(X,Set1);member(X,Set2);member(X,Set3)),
(...action...).
Note:
Use memberchk/2 (which fails or succeeds-once) instead of member/2 (which fails or succeeds-and-then-tries-to-succeed-again-for-the-rest-of-the-set) to make the program deterministic in its decision whether member(X,L).
Similarly, "cut" after the clause guard to tell Prolog that if a guard of one clause succeeds, there is no point in trying the other clauses because they will all turn out false: member(X,Ys),!,...
Finally, use term comparison == and \== instead of unification = or unification failure \= for delete/3.

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), []).

Prolog beginner, combining rules question?

I'm trying to write a program thats based on these facts:
Boyle was born in 1627 and died in 1691.
Newton was born in 1642 and died in 1727.
(and so on)
I want to create a rule that determines if a person was alive during a specified year. Here is what I've come up with so far:
scientist(boyle, 1627, 1691).
scientist(newton, 1642, 1727).
alive_after(X) :- scientist(A, B, C), B < X.
alive_before(X) :- scientist(A, B, C), C > X.
alive_during(X, Year) :- alive_after(X), alive_before(X).
I believe that my first two rules are correct, but they don't seem to be working as intended when I combine them for my alive_during rule. When I run my code with this as my input:
alive_during(1628).
X = boyle
It doesn't work.
What am I missing here?
Prolog cannot unify variables that are hidden inside predicate bodies. There is no relationship between the A in alive_after/1 and the A in alive_before/1. Prolog actually told you that it didn't know what you were doing when it reported these warnings: `
|: alive_after(X) :- scientist(A, B, C), B < X.
Warning: user://2:19:
Singleton variables: [A,C]
|: alive_before(X) :- scientist(A, B, C), C > X.
Warning: user://2:23:
Singleton variables: [A,B]
|: alive_during(X, Year) :- alive_after(X), alive_before(X).
Warning: user://2:27:
Singleton variables: [Year]
It is extremely important that you read these messages as if they are errors, especially when you are new to Prolog!
The solution is to make it so that Prolog is able to unify the scientists across these predicates:
alive_after(Scientist, Year) :- scientist(Scientist, Birth, _), Year > Birth.
alive_before(Scientist, Year) :- scientist(Scientist, _, Death), Year < Death.
alive_during(Scientist, Year) :-
alive_before(Scientist, Year), alive_after(Scientist, Year).
You may also find that it is a little easier to follow the logic when you give your variables meaningful names. I am guilty of using extremely terse variable names when writing very general predicates, but these are actually very specific predicates and a good name can help you understand the structure of what you're doing. It would be, I think a little harder to see how this is more correct than what you wrote:
alive_after(A, X) :- scientist(A, B, _), X > B.
alive_before(A, X) :- scientist(A, _, C), X < C.
alive_during(A, X) :- alive_before(A, X), alive_after(A, X).
With better names, it is much easier to see why your original code is incorrect, because the scientist is not actually shared between the alive_before/2 and alive_after/2 calls.
Another hint that you were confused was that this response to a query makes no sense:
?- alive_during(1628).
X = boyle
Where did X come from? Variables get unified with values from the query, they do not arrive from inside predicate bodies.
An even more direct solution would be to use Prolog's built-in between/3 predicate:
alive_during(Scientist, Year) :-
scientist(Scientist, Birth, Death),
between(Birth, Death, Year).
This has the added advantage that it will actually generate solutions for you:
?- alive_during(boyle, X).
X = 1627 ;
X = 1628 ;
X = 1629 ;
X = 1630 ;
X = 1631 ;
The other solution does not have this property. There are interesting predicates you can write if you do have this generating property, such as contemporaries/2:
contemporaries(S1, S2) :-
alive_during(S1, Y),
alive_during(S2, Y),
dif(S1, S2).
Which generates a lot of not interesting copies of solutions, but you can get rid of them by using setof/3:
?- setof(X-Y, contemporaries(X, Y), Contemporaries).
Contemporaries = [boyle-newton, newton-boyle].

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,_) , []).

Resources