CS470/570 Artificial Intelligence
Program #3: It's only logical!
(a.k.a., knowledge-based agents built on FOPL in Prolog)
Overview:
Now that we have a fundamental understanding of how logics work and, specifically, how First-Order Predicate Logic (FOPL, the book used FOL) works, we need to get our hands dirty to practice and deepen that understanding somewhat. In theory, we'd love to build our own logical reasoning system in Python...that would be really cool, and isn't actually all that hard for a basic version. But there are a lot of details involved, and we're really less interested in implementing a logic programming environment, and more interested in seeing how such an environment could allow "reasoning" to happen.
Fortunately, there is a whole language that is based, in essence, on an implementation of a backward-chaining logical proof processes: Prolog. It works for Propositional Logic (relatively easy), but also works for FOL with universal quantification and all the bells and whistles...so it's just what we need. Our aim, therefore, is to:
- express a simple world of facts and inference rules in FOL...and then translate this knowledge into a Prolog program.
- and then use the Prolog solver to answer some questions about the situation, deducing various conclusions not directly present as base facts.
- Analyze the problem-solving behavior of the Prolog solver system a bit, to make the connection between what we've been working through in lecture/book, and what the Prolog solver is doing.
The advantage of using an existing logic solver (i.e., Prolog in this case) is that we can focus on the logic and the inferencing. The disadvantage is that -- because Prolog is doing a bunch of the work for us -- we risk losing track of the actual logic solving process that is going on within Prolog to arrive at the answers. We will try to address this disadvantage by taking a look under the hood as part of the write-up questions.
The Problem:
It would be lovely to use Prolog to implement a "tour guide" in our dangerous Wumpus cave, i.e., an agent that we can ask questions about the board as we go along (e.g. Is square [x,y] safe?). This is a little bit harder than I'd like, however, given that we are Prolog beginners at this point (anyone that wants to tackle it as E.C. is welcome to).
Our focus, therefore, will be on solving a very straightforward problem centered around two problem domains introduced in the book:
- The math example with the Peano Axioms. You might remember these from your math class, but you probably never considered the coolness of this before. Imagine: all of mathematics (itself a formal symbolic language) has its semantics based on these few tiny postulates. We will implement a little (<10 predicates) logic system built on a few of these.
- The "family kinship" example introduced in Section 8.3.2. Specifically, the challenge is to solve Problem 8.14 from the back of the chapter...and then we'll extend and build on this a little. After all, if you can solve kinship questions about one family, you can easily use the same program to answer questions about *any* family tree!
These problems are dead simple but, as you might expect, this will take some exploration of Prolog and careful thinking about the FOPL "knowledge" that you give it. It will definitely take some time to get your head around what "programming" means in a declarative world! Plus there is your analysis and write-up of what you've done, where you investigate how the logic solver is doing its work, and why it is finding the solutions that it finds. So, as usual, you'll want to start early and plan your time wisely!
The Assignment:
As indicated above, there are two parts to this assignment. The order in which you tackle them doesn't really matter; they are more or less independent and neither one is really a "warm-up" for the other. Part 1 is like 8 lines of code, but requires more thinking. Part 2 has lots of rules/facts but they are all pretty straightforward.
Part1: Math based on the Peano Postulates. Read section 8.3.3 carefully. It's pretty cool: it says that given a definition of what a natural number is and a successor function, you can define most of mathematics. So let's do a little of that: Let's take FOPL definitions of natural numbers and a successor function, and use them to develop working definitions of addition, multiplication and exponentiation, just like the book promises is possible. Here are detailed constraints:
- You must start by producing the FOL definitions for each: NatNum(x), Successor(x), plus(x,y), mult(x,y), and exp(x,y). The book gives you the first three, you do the rest. You will need these for your write-up, and having correct FOL statements will be a substantial piece of your score!
- Your implementation of these must match the FOL definitions closely, i.e., must solve them in the elegant recursive fashion they dictate. So no doing mult(X,Y,OUT) :- OUT is X*Y! We want to implement the postulates in a logic system that proves how Peano's postulates work! (i.e. recursion!). The one (and only) place you could use the "is" (as in Y is X+1) keyword to do "math" is in defining the Successor function. Successor is a "basic given" by Peano, i.e., you do need a way to at least compute a successor given a number...so we'll need to do math (just x+1 and/or x-1) there. But that is the only place you may use "is". Note that there is also a built-in successor function that you could integrate into yours (you won't want to use it straight up), although it's probably easier to write your own.
- Here is an output file showing my Peano implementation at work. At the top I've placed my definition of NatNum(x) as a freebie for you to get you on the right track.
Part 2: At play in the forest of family trees. Read section 8.3.2 again to refresh your memory. The concept is pretty straightforward: we want to encode all of our knowledge about how kinship relationship are defined: fathers, mothers, in-laws, etc. Then, separately and as often as we like, we can give our system some set of information that is known about a particular family...maybe it's complete, maybe it's partial, it doesn't matter. Finally, we can then query our system to spit out (via FOL inference) information about the declared family information. Here are the detailed constraints:
- You must start by producing the FOL definitions for each of the predicates below. There are some predicates in the book in Section 8.3.2 that can give you a clue on how these might look.
Define FOL predicates for the exactly the following relationships:
- Ancestral predicates: grandchild(C,A) ; greatgrandparent(A,D) ; ancestor(A,X)
- Immediate family predicates: brother(X,Y) ; sister(X,Y) ; daughter(X,Y) ; son(X,Y); sibling(X,Y)
- Extended family predicates: firstCousin(X,Y) ; brotherinlaw(X,Y) ; sisterinlaw(X,Y) ; aunt(X,Y) ; uncle(X,Y),
For each of these, the semantic of format are the same: Predicate(X,Y) means that "X is the <predicate> of Y"
- In defining these predicates, you may use only the following "base predicates"; meaning that you don't have to define these, because family tree facts will be stated (exclusively) using these predicates: child(x,y), male(x), female(x), spouse(x,y)
- Now implement the above predicates in Prolog. Paste in the FOL version of the predicate as a comment directly above your Prolog definition for it in your code file.
- As a practice dataset, go ahead and input the family tree given in Problem 8.14 as your base set of facts, using the four predicates given in the last bullet. Make sure you keep these separate from your rules! Your code file should have one section commented as "rules of kinship" and a separate group of "Facts about the British Royal family"
- Because "facts" and the domain knowledge (rules) are distinct in logic languages, making your program operate on different input is as simple as giving it a new set of facts! Consider such a "fact base" as just a text file containing a bunch of atomic sentences, one per line. Here is a little fragment of the fact base for problem 8.14 to give you the idea. The first thing you should do is flesh this out completely. I will be giving you a different juicy family file to exercise your code on shortly before the due date, as usual.
Here is a nice little output file showing my kinship solution code hard at work.
To get you up and going fast, here is a little insta-primer I made on how to get going with Prolog.
Your write-up:
In addition to your code and solution print-outs, you'll
need to provide a nice write-up of your solution. Your write-up should be professionally
neat and must include:
- Using correct and exact FOL syntax, give your FOL rules that encode the knowledge for (a) the Peano problem domain; and (b) the Kinship domain. The listing must include exactly the FOL definitions that drive the Prolog code that you produced. For each rule you write down, start by giving the "plain English" translation of what you are attempting to encode, as well as explaining any thoughts or tricky points associated with your encoding.
- In a step-by-step fashion, walk me through the how Prolog goes about finding the answer to "ancestor(Who, eugenie). Your exposition should be clear and written/formatted as an explanatory technical narrative, i.e., like you're writing a textbook or paper and explaining what's going on at each step to your readers. Obviously, your explanation should demonstrate your keen understanding of the inference algorithm being used.
- Isn't it weird how some queries (e.g. brother(Charles,X) ) in my sample output seem to have "duplicate" answers, listing the correct answers *twice*? I wonder why that's happening for some queries but not others? Strange. Investigate this and explain very clearly what exactly is going on.
To turn in:
A professional hardcopy packet with the following items in exactly this order:
- Cover sheet: Name, course, assignment title, date
- Your write-up, typed up, cleanly formatted.
- Printout of your clearly documented code including (clearly labeled) Part 1 and Part2. Important: For each of your predicates, place the FOL version of is as a comment immediately above the predicate. Separate predicate groups (group=several rules for same predicate) by a line of whitespace for readability.
- Printout of your program running the output from the specified queries on the dynamically assigned math problems.
- Printout of your program running the output of the specified queries on the dynamically assigned family tree problem. Both the Math and Tree dynamic problems will be linked here right here before the due date.