State facts with unbound variables - prolog

How would I state things "in general" about the facts? Suppose I need to state "everyone likes the person who likes him/her", and I have a list of people who may or may not like each other.
This is what I tried so far, but it's sure not the way to do it:
likes(dana, cody).
hates(bess, dana).
hates(cody, abby).
likes(first(Girl, OtherGirl), first(OtherGirl, Girl)).
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
because this won't even compile.
everybody([dana, cody, bess, abby]).
likes_reflexive(dana, cody).
hates(bess, dana).
hates(cody, abby).
likes_reflexive(X, Y):- likes(X, Y), likes(Y, X).
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
%% likes_reflikes_reflexive(X, Y):- likes(X, Y), likes(Y, X).
%% user:6: warning: discontiguous predicate likes_reflexive/2 - clause ignored
%% hates(Girhates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
%% user:8: warning: discontiguous predicate hates/2 - clause ignored
Unfortunately I don't understand what the warnings say. Hope it makes my intention more clear. I.e. by stating one fact, I also want to state the other related fact.

If you want to change your knowledge base dynamically, you can use asserts. If you want to modify existing predicate, you should define it as dynamic, e.g. :- dynamic(likes/2).. If predicate is undefined, you can omit it.
add_mutual_likes(X, Y) :- asserta(likes(X, Y)), asserta(likes(Y, X)).
:- initialization(add_mutual_likes(dana, cody)).
initialization/1 calls add_mutual_likes(data, cody) goal when file is loaded. add_mutual_likes/2 adds two facts to a database. asserta/1 converts it's argument into a clause and adds it to a database.
| ?- [my].
yes
| ?- listing(likes/2).
% file: user_input
likes(cody, dana).
likes(dana, cody).
yes
| ?- likes(cody, dana).
yes
| ?- likes(dana, cody).
yes
| ?- add_mutual_likes(oleg, semen).
yes
| ?- listing(likes/2).
% file: user_input
likes(semen, oleg).
likes(oleg, semen).
likes(cody, data).
likes(data, cody).
yes
I use gprolog.

Let's start with the warnings. They are merely "style" suggestions. They are telling you that all the definitions for likes and hates should be together. Trust me if you have a big Prolog program it becomes a nightmare to go around tour code to get the full definition of your predicate. It would be like writing half a function in C++ and finish it in another file.
Now, you want to say "everyone likes the person who likes him/her". I'm not sure why you are using that function "first" in the code. This would be sufficient:
likes(dana, cody).
likes(Girl, OtherGirl) :- likes(OtherGirl, Girl).
The second clause reads "Girl likes OtherGirl if OtherGirl likes Girl. This won't work.
If you ask your program "is it true that cody likes dana"
? likes(cody, dana)
Prolog will reason like this:
The answer is yes if dana likes cody (using the second clause).
Yes! Because dana likes cody (using the first clause).
This is not enough to make it a correct program. Since we are in Prolog you can say: "give me another solution" (usually by entering ";" in the prompt).
Prolog will think "I only used the first clause, I haven't tried the second".
The answer is Yes also if dana likes cody (using the second clause).
The answer is Yes according to the second clause, if cody likes dana.
But that's our initial query. Prolog will give you the same answer again and again, looping forever if you asked for all the solutions.
You can do two things here. The first is telling Prolog that one solution is enough. You do this adding a "!" (that basically says, clear all the open branches left to explore).
likes(dana, cody) :- !.
likes(Girl, OtherGirl) :- likes(OtherGirl, Girl).
Another alternative is to "stratify the program".
direct_likes(dana, cody).
likes(Girl, OtherGirl) :- direct_likes(OtherGirl, Girl), !.
likes(Girl, OtherGirl) :- direct_likes(Girl, OtherGirl).

What you want is a fact where Prolog does not care about the order of arguments. Alas, something like that does not exist. What you can do instead is define facts where the implied meaning is that it is valid for all argument orders (like_each in the example below). But of course, you cannot use these facts in that way. Instead, you define the actual predicate to try (hence the or ;) all possible argument orders.
Thus, the solution is:
%% bi-directional like
like_each(dana, cody).
likes(A, B) :- like_each(A, B); like_each(B, A).
%% optional: one-directional like
% likes(cody, sarah).
Also, be careful with
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
If both variables are unbound (e.g., ?- hates(A,B)), it will always fail. This happens because Prolog first tries to find a match for likes, which always succeeds for two variables, and then negates the result. Thus, you cannot use hates to find all pairs who don't like each other.

Related

Representing truth regarding beliefs in prolog

How to make this (or something similar) work in Prolog:
belief(john,red(apple)).
belief(peter,red(apple)).
X :- belief(john,X), belief(peter,X).
And get true. for the following query (while consulting above):-
?- red(apple).
First, it's useful to define a little helper to capture when all (relevant) persons believe something:
all_believe(Belief) :-
belief(john, Belief),
belief(peter, Belief).
Then you can define, for example:
red(Object) :-
all_believe(red(Object)).
green(Object) :-
all_believe(green(Object)).
And with your given set of beliefs you get:
?- red(apple).
true.
?- green(apple).
false.
This works. It requires you to define similar rules for any term that you want to use as a belief.
You can make this a bit shorter with macro definitions using term_expansion:
term_expansion(declare_belief(Belief),
Belief :- all_believe(Belief)).
This means that every top-level definition in your source code of the form declare_belief(Belief) should be treated as if you had written Belief :- all_believe(Belief) instead (with the variable Belief substituted appropriately).
So now you can just write this:
declare_belief(red(_)).
declare_belief(green(_)).
and it will be treated exactly like the longer definitions for red(Object) and red(Object) above. You will still have to write this kind of declaration for any term that you want to use as a possible belief.
Prolog does not allow the head of a rule to be just a variable. The head must be a nonvar term, whose functor (i.e., name and arity) identifies the predicate being defined. So, a possible solution would be something like this:
true_belief(X) :-
belief(john, X),
belief(peter, X).
belief(john, red(apple)).
belief(peter, red(apple)).
Examples:
?- true_belief(red(apple)).
true.
?- true_belief(X).
X = red(apple).

Accepting 2 different colors, but not the same colors

I'm trying out an exercise where I have to write the predicate, colors/2 (or colors(C1,C2) :- ...) that runs like the following:
?- colors(red,blue).
true.
?- colors(red,red).
false.
?- colors(blue,blue).
false.
So, essentially, I have to write my predicate in a manner where it doesn't accept when you enter the same color twice.
I am defining my facts to be:
col(red,blue).
col(purple,orange).
col(green, yellow).
I am making my predicate to be:
colors(X,Y) :- (col(X,Y); col(Y,X)) not (col(X,X); col(Y,Y)).
I don't understand why my predicate won't work. It is returning a syntax error with "Operator Expected." I am saying that it doesn't matter in what order you write the facts. Meaning, you can say colors(red,blue) or colors(blue,red), but you can't query colors with the same name without it returning false.
I would like to know:
Why this isn't a valid expression.
What I can do to fix the problem.
A couple of things:
You're missing a comma (,) before not and not/1 expects a single term in parentheses, so use more parentheses:
colors(X,Y) :- (col(X,Y); col(Y,X)), not( (col(X,X); col(Y,Y)) ).
As #PauloMora indicated, not/1 is deprecated in favor of the ISO \+/1, so better would be:
colors(X,Y) :- (col(X,Y); col(Y,X)), \+ (col(X,X); col(Y,Y)).
Then looking at col(X,X) and col(Y,Y), there are no facts or predicates where col(X,X) would be true (both arguments are the same). So each of these will always be false, and \+ (col(X,X); col(Y,Y)) will always be true. So the expression is superfluous, and your predicate becomes (at least with the pattern established in your current set of facts):
colors(X,Y) :- col(X,Y) ; col(Y,X).
Since you don't have any facts stipulated with matching colors (col(x,x)), then queries like col(red, red) will fail anyway.
Per the recommendation by #false, for an integrity check on equality of X and Y, the appropriate mechanism would be dif(X, Y):
colors(X, Y) :- (col(X, Y) ; col(Y, X)), dif(X, Y).
The parentheses are desired since , has higher precedence than ;. This would guard against the case where you happened to have a fact or predicate col/2 in which both arguments were the same (identical or unified).

How can I ask questions on a family tree in Prolog?

I'm currently trying to learn how to use Prolog. I have SWI-Prolog version 6.2.6 installed.
It seems to run:
?- 3<4.
true.
?- 4<3.
false.
As a first example, I was trying to implement the possibility of asking questions about a family tree. So I've started with this, stored in family.pl:
father(bob,danna).
father(bob,fabienne).
father(bob,gabrielle).
mother(alice,danna).
mother(alice,fabienne).
mother(alice,gabrielle).
father(charlie,ida).
father(charlie,jake).
mother(danna,ida).
mother(danna,jake).
father(edgar,kahlan).
mother(fabienne,kahlan).
father(hager,luci).
mother(gabrielle,luci).
male(X) :- father(X,_).
female(X) :- mother(X,_).
But when I try to load this with consult(family). I get:
?- consult(family).
Warning: /home/moose/Desktop/family.pl:7:
Clauses of father/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:9:
Clauses of mother/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:11:
Clauses of father/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:12:
Clauses of mother/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:13:
Clauses of father/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:14:
Clauses of mother/2 are not together in the source-file
% family compiled 0.00 sec, 17 clauses
true.
I don't understand what is the problem here. I've found some results that mentioned that - cannot be used in identifiers, but I didn't use - in an identifier.
Question 1: What causes the Warning from above? How can I fix it?
But there are only warnings, so I've continued with
?- female(fabienne).
true.
?- male(fabienne).
false.
Ok, this seems to work as expected.
Then I've added
male(jake).
female(ida).
female(kahlan).
female(luci).
brother(X,Y):-
male(X),
(mother(Z,X)=mother(Z,Y);father(Z,X)=father(Z,Y)).
and tried:
?- brother(jake,ida).
false.
Why isn't this true?
Question 2: What is the problem with my brother rule?
Your first question is answered here.
As for the second, you're thinking in terms of functions instead of relations.
mother(Z,X) = mother(Z,Y)
is the same as saying X = Y because it compares two terms, without interpreting them. If you want Z to be the mother of both X and Y, you need a conjunction:
mother(Z, X), mother(Z, Y)
the warrning can be fixed if you move all the father and mother declarations together (do not mix them)
In brother definition you use = and you should use a logical AND so use ,(comma)
also you need to tell prolog that jake is a male because he can't figure it out from those rules
I split the brother definition in 2 because is more clear and this is like a logical OR (the first definition is valid or the second one)
father(bob,danna).
father(bob,fabienne).
father(bob,gabrielle).
father(charlie,ida).
father(charlie,jake).
father(edgar,kahlan).
father(hager,luci).
mother(alice,danna).
mother(alice,fabienne).
mother(alice,gabrielle).
mother(danna,ida).
mother(danna,jake).
mother(fabienne,kahlan).
mother(gabrielle,luci).
male(jake).
male(X) :- father(X,_).
female(X) :- mother(X,_).
brother(X,Y):-
male(X),
mother(Z,X),mother(Z,Y).
brother(X,Y):-
male(X),
father(Z,X),father(Z,Y).
For test run
brother(X,Y).
for more results you need to add who is male opr female for those childs like I did for jake

Using cuts in prolog to select facts from database

I'm supposed to use Prolog cuts to get the first , the second and the last fact from the facts database , I found a way to get the first and the second but I can't find a solution for retrieving the last fact here is an example :
P(jack).
P(john).
P(alice).
P(sarah).
P(kyle).
Selecting the first fact only : first(X):-P(X),!.
Selecting the second fact only : second(Y):-P(X),P(Y),X\=Y,P(Y),!.
Selecting the last fact only : ?
I can't see a way without using negation, an accumulator, and the service predicate member, but since negation (by failure) is implemented with cuts, here is my bet:
last_(Y) :- collect([], [Y|_]).
collect(Seen, L) :-
p(X), \+ member(X, Seen), collect([X|Seen], L).
collect(All, All).
Instead of \+ member(Elem,List) (read Elem is not in List), you could implement a not_contains/2, with explicit cut inside.
BTW your second/1 predicate contains a redundant call: should be
second(Y):-p(X),p(Y),X\=Y,!.

User input in prolog

I want to write a program that sets cold to true if the user enters winter and sets warm to true if the user enters summer. This is what I have so far:
start :- write('What season is it?: '), read(X), season(X).
cold :- season(winter).
warm :- season(summer).
However when I query start and enter winter for the season I get an error that season/1 is undefined. To fix this I tried changing my code to the following:
start :- season(X).
season(X) :- write('What season is it?: '), read(X).
cold :- season(winter).
warm :- season(summer).
Now when I query start it asks for the season as expected and I entered winter. I queried cold expecting a return of true since I figured season(winter) would be true, but instead I was again prompted to answer "what season is it?". How can I make this simple program work?
It looks that what you want to do is kind of "forward chaining". So you
want to be able to add a fact to the knowledge base, and then see
additional facts pop up.
Such things were very fashionable in the 80's when expert systems emerged
and can also be done with Prolog. Here is an article roughly describing the
difference between "backward chaining" and "forward chaining":
Logic Programming Associates Ltd.
ProWeb: Expert System
I have tried to remodel something similar to the above forward chaining
via the clause expansion mechanism that is available in many Prolog
systems and via an agenda that is held in the knowledge base as ordinary
facts. The expansion mechanism changes a rule of the following form:
P :- A
Into a rule where X is a fresh variable not occuring in P or A:
delta(X, P) :- A_new(X)
Where A_new is a condition that says that P is new when a new fact
X has arrived. Lets consider your example:
?- ['delta.p'].
?- [user].
:- forward season/1.
:- forward cold/0.
:- forward warm/0.
cold :- season(winter).
warm :- season(summer).
^D
These two Prolog rules would be turned into the following delta/2 rules
via the rewriting. Since the rules are very simple, the delta/2 rules
are also very simple:
?- listing(delta/2).
delta(X, cold) :-
X = season(winter).
delta(X, warm) :-
X = season(summer).
Here is an example session:
?- list.
Yes
?- volunteer(season(winter)).
Yes
?- list.
season(winter).
cold.
Yes
The rewriting in delta.p is very primitive. The rewriting can be enhanced
to support rigid predicates and to support the dynamic removal of facts.
Possible additional application areas of the "forward chaining" are then:
Natural Language Processing
Constraint Solving
Best Regards
The current delta/2 rewriting after a few more years development:
http://www.jekejeke.ch/idatab/doclet/blog/en/docs/15_min/02_reference/minimal/delta.html
An Article showing that a Constraint Store can be modelled:
https://plus.google.com/+JekejekeCh/posts/8oHErwopKxK
P.S.: The predicate name volunteer/1 stems form Nexpert Object,
an expert system shell in use in the 80's and 90's.
NEXPERT OBJECT version 3.0, Jean-Marie Chauvet, Neuron Data, Inc.
P.P.S.: But you might want to lookup newer things, such as Rete-NT, or OWL:
http://answers.semanticweb.com/questions/3304/forward-vs-backward-chaining
Because "cold/0" and "warm/0" take no arguments, it is impossible to change the result at runtime without altering their definition at runtime. The simplest way to do this is just to add or remove the facts "cold." and "warm." depending on user input, using assert/1 and retractall/1.
start :- write('What season is it?: '), read(X), season(X).
season(summer) :- retractall(cold), assert(warm).
season(winter) :- retractall(warm), assert(cold).
While defining the facts, you need to change the syntax a little bit.
start(Y) :- write('What season is it?: '), read(X), nl, season(X,Y).
season(winter,cold).
season(summer,warm).
Note that the user needs to end the input with a full stop. (eg. winter.)
season(winter,cold).
season(summer,warm).
start:-write('What season is it?'),write(' '),read(X),season(X,Y),write(Y).
The output will be look like this:
?- start.
What season is it? winter.
cold
true.

Resources