How would you go about implementing this example of Resolution in Prolog? - prolog

This is perhaps one of the simpler examples of Resolution in FOL there is, but I'm having some trouble wrapping my head around how this would look like written in Prolog:
∀x. GradStudent(x) ⊃ Student(x)
∀x. Student(x) ⊃ HardWorker(x)
GradStudent(sue)
The query is KB |= HardWorker(sue). I can see how you'd go about manually doing the Resolution, but I'm lost as to how this would look like in Prolog.
Where I'm getting stuck is the HardWorker predicate, there is no explicit definition of HardWorker and of course that is not valid in Prolog.

Turn the arrow around:
hardworker(X) :- student(X).
student(X) :- gradstudent(X).
gradstudent(sue).
So we go on and ask if sue is a hard worker:
?- hardworker(sue).
true.
Note the incompleteness of this all:
There are other hard workers than just students. There are other kinds of students than grad students. And Sue is probably not the only grad student.

Related

Prolog taking inverse of a predicate

I got a database that looks like
hasChild(person1, person2).
hasChild(person1, person3).
hasChild(person4, person5).
Which means that (for example) person1 has child named person2.
I then create a predicate that identifies if the person is a parent
parent(A):- hasChild(A,_).
Which identifies if the person is a parent, i.e. has any children
Then I try to create a predicate childless(A) that should return true if the user doesn't have any children which is basically an inverse of parent(A).
So I have 2 questions here:
a) is it possible to somehow take an "inverse" of a predicate, like childless(A):-not(parent(A)). or in any other way bypass this using the hasChild or any other method?
b) parent(A) will return true multiple times if the person has multiple children. Is it possible to make it return true only once?
For problem 1, yes. Prolog is not entirely magically delicious to some because it conflates negation and failure, but you can definitely write:
childless(X) :- \+ hasChild(X, _).
and you will see "true" for people that do not have children. You will also see "true" for vegetables, minerals, ideologies, procedures, shoeboxes, beer recipes and unfeathered bipeds. If this is a problem for you, a simple solution is to improve your data model, but complaining about Prolog is a very popular alternative. :)
For problem 2, the simplest solution is to use once:
parent(A) :- once(hasChild(A, _)).
This is a safer alternative to using the cut operator, which would look like this:
parent(A) :- hasChild(A, _), !.
This has a fairly significant cost: parent/1 will only generate a single valid solution, though it will verify other correct solutions. To wit:
?- parent(X).
X = person1.
Notice it did not suggest person4. However,
?- childless(person4).
true.
This asymmetry is certainly a "code smell" to most any intermediate Prolog programmer such as myself. It's as though Prolog has some sort of amnesia or selective hearing depending on the query. This is no way to get invited to high society events!
I would suggest that the best solution here (which handles the mineral/vegetable problem above as well) is to add some more facts about people. After all, a person exists before they have kids (or do they?) so they are not "defined" by that relationship. But continuing to play the game, you may be able to circumvent the problem using setof/3 to construct a list of all the people:
parent(Person) :-
setof(X, C^hasChild(X, C), People),
member(Person, People).
The odd expression C^hasChild(X, C) tells Prolog that C is a free variable; this ensures that we get the set of all things in the first argument of hasChild/2 bound to the list People. This is not first-order logic anymore folks! And the advantage here is that member/2 will generate for us as well as check:
?- parent(person4).
true.
?- parent(X).
X = person1 ;
X = person4.
Is this efficient? No. Is it smart? Probably not. Is it a solution to your question that also generates? Yes, it seems to be. Well, one out of three ain't bad. :)
As a final remark, some Prolog implementations treat not/1 as an alias for \+/1; if you happen to be using one of them, I recommend you not mistake compatibility with pre-ISO conventions for a jovial tolerance for variety: correct the spelling of not(X) to \+ X. :)
Here's another way you could do it!
Define everything you know for a fact as a Prolog fact, no matter if it is positive or negative.
In your sample, we define "positives" like person/1 and "negatives" like childless/1. Of course, we also define the predicates child_of/2, male/1, female/1, spouse_husband/2, and so on.
Note that we have introduced quite a bit of redundancy into the database.
In return, we got a clearer line of knowns/unknowns without resorting to higher-order constructs.
We need to define the right data consistency constraints:
% There is no person which is neither male nor female.
:- \+ (person(X), \+ (male(X) ; female(X))).
% Nobody is male and female (at once).
:- \+ (male(X), female(X)).
% Nobody is childless and parental (at once).
:- \+ (childless(X), child_of(_,X)).
% There is no person which is neither childless nor parental.
:- \+ (person(X), \+ (childless(X) ; child_of(_,X))).
% There is no child which is not a person.
:- \+ (child_of(X,_), \+ person(X)).
% There is no parent which is not a person.
:- \+ (child_of(_,X), \+ person(X)).
% (...plus, quite likely, a lot more integrity constraints...)
This is just a rough sketch... Depending on your use-cases you could do the modeling differently, e.g. using relations like parental/1 together with suitable integrity constraints. YMMY! HTH

Prolog negation with more solutions

I've got a little problem and don't know where to find a solution. You probably heard of problem of flying birds:
bird(eagle).
bird(penguin).
can_fly(penguin):-!,fail.
can_fly(X):-bird(X).
I tried to modify and use this knowledge for some "love story". Imagine this
maried(a, b).
maried(c, d).
lovers(a, d).
likes(X, Y):-maried(X, Y).
Now I want to say something like "If X is maried with Y, but X is Z's lover, then X doesn't like Y, but likes Z".
I tried this:
likes(X, Y) :- lovers(X, Y).
likes(X, Y) :- maried(X, Y), lovers(X, _),!,fail.
likes(X, Y) :- maried(X, Y).
It works unless I want to evaluate a goal
likes(A, B).
If there is more facts in the database and Prolog finds first cheater, it will stop backtracking and I can't find any other solution. Maybe it will be obvious after, but now I have nothing on my mind..
Thanks in advance (and maybe sorry for my English :))
Already your first example is not too useful. You can ask:
is the eagle a bird?
is the penguin a bird?
can the eagle fly?
can the penguin fly?
but you can't even ask, "which birds can fly?":
?- can_fly(Bird).
false.
If you would want to be able to ask more general question, like, "which birds can fly?", or "which birds cannot fly?", you would need to either explicitly list the flying or non-flying birds. As most birds can fly, let's list explicitly the non-flying birds:
bird(eagle).
bird(penguin).
bird(ostrich).
bird(dodo).
bird(sparrow).
bird(pigeon).
flightless_bird(penguin).
flightless_bird(ostrich).
flightless_bird(dodo).
bird_of_pray(eagle).
extinct(dodo).
extinct(wooly_mammoth).
can_fly(Bird) :-
bird(Bird),
\+ flightless_bird(Bird).
extinct_bird(Bird) :-
bird(Bird),
extinct(Bird).
Note the use of the ISO predicate for negation, \+/1. It is true if the goal cannot be proven. It is much cleaner than the fail-cut combination you are using.
How you organize your knowledge is a totally different question. The example I have given is incomplete and not necessarily the best way to do it. As a general rule, you should try to keep your data in a normalized form, with facts and clauses of facts playing the role of tables and table rows.
Hopefully this answer points in the right direction.
The cut-fail thing is an antipattern that's misleading you. It would be much better to say something in the original like this:
bird(eagle).
bird(penguin).
flightless(penguin).
can_fly(X) :- bird(X), \+ flightless(X).
See #boris's answer for an excellent and much more detailed discussion of this problem.
So, you have this database. I'm going to put names in so that I can comprehend it a little better.
married(bill, hillary).
married(barack, michelle).
lovers(bill, michelle).
Now what you want to say is that married people like each other, unless they're cheating. Well, it would be better to define these terms in Prolog so we can reason with them. Create the language we need to use to define our domain. That's going to look like this:
cheating(Cheater, SpitedSpouse, Lover) :-
married(Cheater, SpitedSpouse),
lovers(Cheater, Lover),
SpitedSpouse \= Lover.
Now it's much easier to define likes/2!
likes(Husband, Wife) :- married(Husband, Wife), \+ cheating(Husband, Wife, _).
likes(Husband, Lover) :- cheating(Husband, _, Lover).
We can even define dislikes properly:
dislikes(SpitedSpouse, Lover) :- cheating(_, SpitedSpouse, Lover).
dislikes(Cheater, Spouse) :- cheating(Cheater, Spouse, _).
Look at what we've learned: what you really have is a marriage relationship between two people, or a cheating relationship between three people, and of your other predicates are just projections of these two fundamental relationships. That's pretty neat, yeah? :) cheating/3 is basically the classic "love triangle." That suggests ways we could evolve the program to be more potent: handling love quadrilaterals, of course! And so forth.
Notice something interesting? This code can't "catch" michelle—isn't she just as culpable as bill? This underscores a bigger problem: the gender/orientation limitation of the predicates. I think it helps to have concrete nouns like these to see the logical relationships, but it's a dangerous shortcut that should be resolved. An easy way to handle the logic would be to create a predicate like this:
spouse(X, Y) :- married(X, Y)
spouse(X, Y) :- married(Y, X).
Of course you then have to do more checks to make sure you don't have the same people repeated, but it's not hard, and then change your variables to be less gender-specific.

evaluating possible situations using prolog

I am trying to see the possible situations for the following facts.
don likes cain
bob does not like don
cain does not like aron
nobody likes someone who does not like him
aron likes everyone who likes bob
don likes everyone bob likes
everybody likes somebody
I have just started learning prolog and I am trying to implement this in prolog and see how many possible situations arise. I scanned through few prolog threads here and I am also using the book "Learn prolog now" . So here is my best attempt to come up with the code.
likes(don, cain).
likes(aron,W):- likes(W,bob).
likes(don,M):- likes(bob,M).
(likes(aron,aron);likes(aron,bob));(likes(aron,cain);likes(aron,don)).
(likes(bob,aron);likes(bob,bob));(likes(bob,cain);likes(bob,don)).
(likes(cain,aron);likes(cain,bob));(likes(cain,cain);likes(cain,don)).
(likes(don,aron);likes(don,bob));(likes(don,cain);likes(don,don)).
not(likes(bob,don)).
not(likes(cain,aron)).
not(likes(Y,X)) :- not(likes(X,Y)).
When I run this in swipl compiler in Ubuntu Linux (which is in VirtualBox inside winxp), I get following errors
?- [test].
ERROR: /home/test.pl:11:
'$record_clause'/2: No permission to modify static_procedure `(;)/2'
ERROR: /home/test.pl:13:
'$record_clause'/2: No permission to modify static_procedure `(;)/2'
ERROR: /home/test.pl:15:
'$record_clause'/2: No permission to modify static_procedure `(;)/2'
ERROR: /home/test.pl:17:
'$record_clause'/2: No permission to modify static_procedure `(;)/2'
% test compiled 0.00 sec, 1,616 bytes
true.
So can you help me with this...... I have made use of not as a predicate here, some threads on the net seem to mention it.
In Prolog we have a fairly natural representation of what's true, but when faced with negative information (say negation), we must adapt our intuition to the restricted computation model that Prolog offer.
In particular, Prolog semantic being based on closed world assumption, we could be tempted to omit as irrelevant proposition like bob does not like don, because this just states the absence of the positive clause 'bob likes don'. Such absence is indeed 'absorbed' in Prolog proof search, and formalized by a 'procedural' definition of not (the only available in Prolog, but with an operator less reminiscent of 'human' interpretation, i.e. \+ means not):
\+ X :- call(X), !, fail.
\+ X.
See this page for further explanation about.
All this introductory to say that I would introduce a not_like/2 to represent explicitly the 'negative' knowledge. Syntactically we get:
likes(don, cain).
not_likes(bob, don).
not_likes(cain, aron).
not_likes(X, Y) :- \+ likes(Y, X).
likes(aron, X) :- likes(X, bob).
likes(don, X) :- likes(bob, X).
likes(_, _).
Now those statements make sense? Prolog takes the viewpoint of a practical approach to logic programming, and it run queries against such knowledge, that make perfectly sense for some programming task...
edit suggested by comment, I think that instead of likes(_,_)., a better code for everybody likes somebody should be
likes(X, Y) :- \+ not_likes(X, Y).
This is justified by the last fact, but standalone it not allows to infer not_likes(cain, aron).

Reverse lookup in Prolog? (how do I find everything that is true about X?)

So, let's say I have the following in a Prolog database:
person(john).
person(mary).
happy(john).
It is clear to that if I want to list all people, I can type:
person(X).
But, what if I want to find all the things that are true about john? I cannot do:
X(john).
But the effect I would like is to be able to put in "john" and get back "person" and "happy".
There is clearly another way I could store my information:
is(person, john).
is(person, mary).
is(happy, john).
And then, I can do:
is(X, john).
But I lose some expressiveness here. I really would like to be able to do something like:
X(john).
Any ideas?
Thanks!
Parameterizing a query over predicates (as in finding ∀x over x(...)) is not usually possible natively in PROLOG, as this sort of thing is a second- (or, higher)-order logic operation, whereas PROLOG is based on first-order logic.
There are, however, descriptions of how implementations of higher-order logic functions in PROLOG are possible, at least to a limited extent - there are real uses for such functionality. See The Art Of Prolog, Chapter 16, and Higher-order logic programming in Prolog by Lee Naish.
Hm, from my experience, that's not the typical use case of Prolog. If you want to enumerate all "facts" about John, you would first have to define them as terms, and encode their arity. Then you can use call/N and go down the rabbit hole another notch (from memory with the help of GNU Prolog):
relation(1,person).
relation(2,married).
person(john).
married(john,mary).
? relation(1,X), call(X,john).
X = person
| ?- relation(2,X),call(X,john,Y).
X = married
Y = mary
Note that using call has many interesting issues and potential for runtime errors.
This is an approximation:
all_predicates(Term) :-
current_predicate(_, Pred), %% match Pred to any currently defined predicate
\+ predicate_property(Pred, built_in), %% filter out the built-in predicates
functor(Pred, Name, 1), %% check that Pred has 1 argument and match Name to its name
Goal =.. [Name, Term], %% construct the goal Name(Term)
call(Goal). %% Note that if Pred has side effects, they will happen.

Prolog — symmetrical predicates

I have to simulate family tree in prolog.
And i have problem of symetrical predicates.
Facts:
parent(x,y).
male(x).
female(y).
age(x, number).
Rules:
blood_relation is giving me headache. this is what i have done:
blood_relation(X,Y) :- ancestor(X,Y).
blood_relation(X,Y) :- uncle(X,Y)
; brother(X,Y)
; sister(X,Y)
; (mother(Z,Y),sister(X,Z))
; (father(Z,Y),sister(X,Z))
; (father(Z,Y),brother(X,Z)).
blood_relation(X,Y) :- uncle(X,Z)
, blood_relation(Z,Y).
and I am getting i think satisfactory results(i have double prints - can i fix this), problem is that i want that this relation be symmetrical. It is not now.
blood_relation(johns_father, john):yes
blood_relation(john,johns_father): no
so..is there a way to fix this.
And i need query: All pairs that are not in blood_relation..
Update:
What kinds of relationships is the first statement supposed to satisfy?
blood_relation(X,Y):-blood_relation(X,Y).
sorry..it is a bad copy/paste..it
blood_relation(X,Y):-ancestor(X,Y).
Now fixed above.
here are other rules:
father(X,Y) :-
parent(X,Y),male(X).
mother(X,Y) :-
parent(X,Y),female(X).
brother(X,Y) :-
parent(Z,X),parent(Z,Y),
male(X).
sister(X,Y) :-
parent(Z,X),parent(Z,Y),
female(X).
grandFather(X,Y) :-
parent(Z,Y),parent(X,Z),
male(X).
grandMother(X,Y) :-
parent(Z,Y),
parent(X,Z),female(X).
uncle(X,Y) :-
mother(Z,Y),brother(X,Z).
ancestor(X,Y) :-
ancestor(X,Y).
ancestor(X,Y) :-
parent(X,Z),ancestor(Z,Y).
Mother's brother is in uncle definition. It's kind of strange. I've got rules that I need to implement, and I don't know how I can implement rules besides that. I'm just confused.
Any idea how to make blood_relation symmetric? And not_blood_relation is a new rule. And I need query. This one is really giving me headache. Maybe because relation is written like crap.
And there are no more facts. That's all. All rules, and all facts.
query.. not(blood_relation(X,Y)) doesn't work, and I really don't know why.
For example query:
age(X,Y), Y>18,
not(parent(X,Z)),write(X),nl,fail.
works just fine
The naive solution to making a particular predicate symmetric isn't that far from a decent one. For the sake of generality, let's look at a friendship relation so people don't get tripped up on uncles and the like.
Here are some facts detailing a friendship relation (where, say, the numbers are user ids and the particular ordering of the arguments came from who initiated the friendship).
friends(1,2).
friends(5,2).
friends(7,4).
You'd initially think a rule like "friends(A,B) :- friends(B,A)." would fix things right up, but this leads you to infinite recursion because it tells prolog that if it just swaps the argument one more time it might just work. There is a predicate called "#</2" that tells you whether one term (even a variable) comes before another in the "standard order of terms". The technical meaning isn't all that important here, but what we care about is that for two different terms it is only true for one ordering of them. We can use this to break the infinite recursion!
This single rule will take care of making "friend/2" symmetric.
friends(A,B) :- A #< B, friends(B,A).
As neat as this is, there is an approach way you should take for large projects. Recall that the ordering of the args in my list of facts had some actual meaning (who initiated the friendship). Adding the final rule destroyed future access to this information and, for other people reading the code, hides the symmetric property in a single line of code which is easy to ignore in the face of a block of hard-coded data.
Condsider the industrial-strength solution:
friended(1,2).
friended(5,2).
friended(7,4).
friends(A,B) :- friended(A,B).
friends(A,B) :- friended(B,A).
It is bulkier, but it reads cleanly without using obscure predicates and retains the original information (which you might want again someday in a real application).
--
As for finding pairs that don't have a specific property, make sure you always include some predicate to provide context in your rule when you use negation to look for actual individuals.
potential_enemies(A,B) :- user(A), user(B), \+ friends(A,B).
A bit looks like a homework, isn't it...
One trick which most of beginners of prolog don't think of is list pattern matching. Think of a tree like [a1,[[a2],[b2,[[e3],[f3]]],[c2]]] as in <tree>=[root,[<tree1>,<tree2>,...]]:
%Y is immediate child of X?
child(X,Y,[X|S]) :- member([Y|_],S).
%pick one tree in S and check
child(X,Y,[X|S]) :- member([Z|SS],S),child(Z,Y,[Z|SS]).
%X and Y end up with same root?
sib(X,Y,[R|T]) :- child(R,X,[R|T]), child(R,Y,[R|T]).
I think you can improve upon this like, using pairs as roots, adding genders, giving names to specific relations of members of the tree...
What kinds of relationships is the first statement supposed to satisfy?
blood_relation(X,Y):-blood_relation(X,Y).
That isn't telling you anything that you don't already "know" and is going to cause you recursion headaches. As for the 'no' answer, is looks like you've already gotten all of the answers from the query that you are going to get, and the interpreter is just telling you that there aren't any more.
You really should post more facts, and the definition of uncle/2, and is there a reason why you're not matching a mother's brother, just her sister? You have lots of other issues to work on :-).
For everything that is not a blood relation, try this:
not_blood_relation(X, Y) :- blood_relation(X, Y), !, fail.
not_blood_relation(X, Y).
And ask yourself why it works!

Resources