Due: November 2 at 11:59 PM
Please submit your code via WebWork (click here for directions). For questions about the assignment contact Ryan Riegel.
The assignment is worth (TBD)% of your final grade.
This project is intended to familiarize you with the use of computers to answer questions about propositional and first-order logic. You will code portions of established reasoning algorithms as well as encode a logic puzzle in a format that a computer can understand. The hope is that, as you work on this assignment, you will see that logical reasoning can be rendered into a relatively simple methodical process.
This project is to be completed in LISP. By now, you should all have enough experience with the language that the following will not pose to much difficulty with regards to programming.
s | -> | atom | complex |
atom | -> | symbol | value |
symbol | -> | p | q_3 | isRaining | ... |
value | -> | t | nil |
complex | -> | (_not s) | (_and s s) | (_or s s) | (_implies s s) | (_iff s s) |
Example: (_iff (_implies p q_3) (_not (_and p (_not isRaining))))
m | -> | (pairs) |
pairs | -> | &epsilon | pair pairs |
pair | -> | (symbol value) |
Example: ((p t) (q_3 nil) (isRaining nil))
Note that &epsilon is used to express nothingness in context-free grammars; the above simply indicates that models are represented with lists of pairs.
s | -> | atom | complex |
atom | -> | (predicate terms) | (_equals term term) |
predicate | -> | Brother | OnHead | Evil | ... |
terms | -> | epsilon | term terms |
term | -> | (_const constant) | variable |
constant | -> | John | Crown | M_1 | ... |
variable | -> | x | y | z | ... |
complex | -> | (_not s) | (_and s s) | (_or s s) | (_implies s s) | (_iff s s) |
(pl-true '(_implies (_or a b) c) '((a t) (b nil) (c t)))should return t (or any non-nil value), but
(pl-true '(_or a (_and b c)) '((a nil) (b nil) (c t)))should return nil. You can assume that the model will always contain all of the symbols used in the sentence.
(pl-symbols '((_implies x y) (_or z (_implies y z)) (_or (_not z) nil) w))should return the list (x y z w). The order of returned symbols does not matter.
(mc-fold f acc0 symbols)If symbols is '(a b), the result of the above should be equivalent to:
(f '((a nil) (b nil)) (f '((a nil) (b t)) (f '((a t) (b nil)) (f '((a t) (b t)) acc0))))The order in which models are processed does not matter. Also note that any input function f used with mc-fold should expect a model for its first argument and the accumulator (a return value of the previous call to f) for its second.
Because there are O(2n) models for n symbols, this function will have an exponential running time. You should, however, try to implement the function such that its space complexity is only O(n) provided that the size of f's results don't grow as the chain shown above evaluates. In other words, use recursion to generate and process possible models rather than creating all models upfront and applying f to them in series. Look at the TT-Entails? function on page 209 of Russell/Norvig for inspiration.
(count-worlds '(x y z u v)) => 32
(enumerate '(a b)) => (((a nil) (b nil)) ((a nil) (b t)) ((a t) (b nil)) ((a t) (b t)))
(filter '(_implies p (_and q r)) '(p q r))) => (((p nil) (q nil) (r nil)) ((p nil) (q nil) (r t)) ((p nil) (q t) (r nil)) ((p nil) (q t) (r t)) ((p t) (q t) (r t)))This function should not rely on enumerate to work, but should instead call mc-fold directly with a slightly more complicated input function.
(mc-entails '((_or x (_or y z)) (_not x) (_implies y z)) 'z) => t (mc-entails '((_implies a b) (_implies b c) a) '(_or c d)) => t (mc-entails '(p (_implies q p)) '(_iff p q)) => nil
(mc-valid '(_or a (_not a))) => t (mc-valid '(_implies (_implies (_implies p q) p) p)) => t (mc-valid '(_iff (_and x (_or y z)) (_or (_and x y) z))) => nil
Some examples of the function's behavior are as follows:
(cnf '(_and (_implies a b) c)) => (_and (_or (_not a) b) c) (cnf '(_not (_or (_not p) q))) => (_and p (_not q)) (cnf '(_or x (_and y (_and z w)))) => (_and (_or x y) (_and (_or x z) (_or x w)))
(cnf-list '(_and (_implies a b) c)) => (((_not a) b) (c)) (cnf-list '(_not (_or (_not p) q))) => ((p) ((_not q))) (cnf-list '(_or x (_and y (_and z w)))) => ((x y) (x z) (x w))Note that the inner lists in this representation (the disjunctions) are refered to as clauses in later parts of this assignment. At this point it is fairly easy to remove meaningless duplicates of the same literal in each clause; you should do this. If you are an over-achiever, go ahead and remove entire clauses if they contain both positive and negative literals for the same symbol, as well as duplicate clauses if they occur.
Two clauses are said to clash on a literal when they contain complementary versions of that literal (e.g., one contains a, and the other (_not a)), and are resolved by unioning all other literals within the clauses. Write a function clash that returns a list of all possible clash resolutions between two clauses, or the empty list if there are none.
(clash '(p q r) '((_not p) q (_not s))) => ((q r (_not s))) (clash '((_not x) y) '(x (_not y))) => ((y (_not y)) ((_not x) x)) (clash '(a b) '(c (_not d))) => () (clash '((_not p)) '(p)) => (())Note that the last two terms are very different from each other. The former indicates that no clashes were possible, with minimal significance to the process of resolution, while the later indicates that the clash resulted in box (yet another way of saying the empty list), meaning that the sentence considered by resolution is in fact unsatisfiable.
These two functions should accept the same arguments as their model checking counterparts, and should have the same behavior. If you're feeling adventurous, compare running times between to two versions for various inputs. Also, keep in mind that resolution is officially a satisfiability checker that operates on a single list of clauses (as returned by cnf-list), so you'll have to process the above inputs in some way before you can pass them off to the pl-resolution function.
(unify '(Knows (_const Bill) (_const Bob)) '(Knows y (_const Bob))) => ((y (_const Bill))) (unify '(Knows (_const Bill) x) '(Knows y (_const Ted))) => ((x (_const Ted)) (y (_const Bill))) (unify '(Gaseous a) '(Gaseous b)) => ((a b)) (unify '(_and (Cat x) (Owns y x)) '(_and (Cat x) (Owns y x))) => nil (unify '(_and (Cat x) (Owns y x)) '(_or (Cat z) (Owns w z))) => failureLook at page 278 of Russell/Norvig for the specifics of this algorithm. Note that in our representation of sentences makes compounds (uses of logical symbols and predicates) look very similar to argument lists. You may want to define a helper function to handle lists, rather than use type checking as indicated in the book. Also, don't worry about standardizing apart; you can assume this is done before your unify function is called. Lastly, keep in mind that you should only substitue variables with terms (in our syntax, this means constants or other variables, but in general function applications are included as well); the structure of sentences in first-order logic should prevent other substitutions from being considered, so you don't need to worry about this too much.
First off, the puzzle provided to you is in English! Define a parameter puzzle-kb that expresses the puzzle as a list of first-order definite clauses (needed for forward-chaining to work). Recall that a definite clause is a disjunction that contains at most one positive term (i.e., only one term that isn't precluded by _not). Alternately, definite clauses may be represented as an implication of a single term by a conjunction of terms, where all terms are positive. In other words, it should either look like
(_or (_or (_or (_not (Fungus x)) (_not (Orange x))) (_not (Slimy x))) (Poisonous x))or like
(_implies (_and (_and (Fungus x) (Orange x)) (Slimy x)) (Poisonous x))Also,
(Consumed (_const Bob) (_const TheMushroom))is considered a definite clause because it is, after all, just one positive term.
The fol-fc-ask function actually operates on list structures that represent definite clauses, but there is a provided function definite-list that converts either of the above into an acceptable format. Make sure to use it in your solve-puzzle function below. Students who have read ahead might find some of this function's components useful in defining cnf. Also note that you must not use any numbers as variable names in your KB; fol-fc-ask uses numbers to standardize apart, and your use of them could cause conflicts. It's okay to use numbers in variables names, just not as them.
(solve-puzzle) => ((Murderer ???) (MurderWeapon ???) (SceneOfTheCrime ???))