Final Projects


The final projects will be due the last day of class, and will be worth 5 weekly assignments. The grade will be based on "correctness" (does it properly accomplish the task or implement the algorithms it was designed for), "style" (efficient, well-written code, modularity, etc.), "testing" (do you show enough capability to show it is functioning correctly and to illustrate the behavior), and, "discussion" (observations, conclusions, and comparisons, as needed).

If you use code from any texts, be sure to document it well, and carefully explain what is going on. Copying of code can definitely be more harmful than helpful. Clean, well laid-out output will be another consideration. Also, be sure to give examples that illustrate the capabilities of your system. If you want me to be impressed by a certain capability, point it out and illustrate it!

None of these topics are carved in stone, so please discuss with me any ideas and/or questions you may have. A brief proposal explaining what project you intend to do is due no later than the week before the projects start (March 28). Of course, however, you may discuss things with me in advance.

1. PROLOG Interpreter. Write a PROLOG interpreter in Lisp. You only need to implement the basic capabilities, and you are free to modify PROLOG syntax somewhat. It should be able to do backward chaining (of course), recursive definitions, search the database for matches as expected, and deal with lists. Other capabilities are optional.

2. Theorem Prover. Write a program that will use resolution theorem proving to verify statements expressed in some representation of First Order Logic in Lisp. Show output of the conversion to clause form separately for a few smaller cases.

3. Rule-based AI Programming Shell. Write a rule-based shell along the lines of the one I discussed in class last semester. For those who were not in my section, this was a basic forward-chaining rule-based system with Lisp-like syntax. You are free to invent your own syntax, of course, but the one discussed last semester was something like this:

(defrule Test

(?X has ?Y children)

(lisp (> ?Y 2))

(?X is employed-as a ?Z)

-->

(?X is in class Foo) )

At the minimum, it should do pattern matching, allow an arbitrary number of patterns on the left and right hand sides, have an inference engine that tries all rules against all facts but keeps the same rule from firing regarding the same facts more than once, allow Lisp expressions on the left or right hand sides, and allow the deleting of patterns from the working memory. You may choose which other capabilities to add. You probably will want to add several of the following: "cant-find" tests, the ability to bind a new variable on the left-hand-side, disjunctive ("or") tests on the left hand side, or other capabilities of your own design.

4. More extensive analysis of heuristic search. Look at various modifications of heuristic search algorithms, and compare the results. You could pick one particular algorithm (A* or IDA*) and look at the costs/benefits of various techniques on two different application domains (e.g. a relatively short, bushy tree like word ladders and a relatively tall narrow tree like the eights puzzle). Alternatively, you could look at a single application domain, but more than one search algorithm. You should collect a large amount of data and graph the results. One common type of graph would have average cost (time in seconds, number of nodes stored, number of nodes examined, etc.) on the Y axis, and solution length on the X axis, with two curves showing results with and without some particular modification. Aplcenmp has a nice X-based graphing package called "xmgr", if you don't have access to an alternative yourself.

Some suggested modifications to investigate:

A*

5. A* and IDA* for Path Planning. For those with access to machines at home or work that have graphics libraries usable from Lisp, using heuristic search for path planning is an interesting application. The idea is to deal with polyhedral obstacles only, and to find a shortest path from point A to point B through a field of the obstacles. Given a node in the search tree, the children of that node are (a) all established points (such as point A or point B), plus (b) object vertices that can be reached in a straight line that does not intersect any other obstacle. A large part of the problem will be to write the geometric code that figures out if a line segment intersects with any object. I would expect the system to draw the obstacles and the path found.

6. Object-oriented Shell. Write a basic object oriented shell along the lines of CLOS. The most basic capabilities should be the inheritance of slots, the ability to override inherited values, and some type of handling of methods.

7. Extended Object-oriented Simulation. Extend the scope of the object-oriented simulation done during the semester by adding much more complex behaviors. You could also combine this with some of the parsing done this semester and the random sentence generator of last semester (if you were in my section) for simple I/O in Natural Language. If you wish to do this, please provide a specific proposal of how you would like to do this, and talk to me before the proposal deadline. Be sure what you propose is a significant extension (on a par with the other projects).

8. Natural Language. Some NLP problem that makes use of the chart parser you wrote during the semester. Some ideas:

9. Alpha-Beta Pruning. Implement alpha-beta pruning for some two person game. Chess or Go are probably two hard, and tic-tac-toe is too easy. If you choose Othello, you will have more extensive requirements since Norvig spends an entire chapter on writing an Othello program. In any case, that chapter will be useful for this project. Other possible games are Qubik (3-D 4-in-a-row tic-tac-toe), Score Four, Connect-Four, Gomoku (5-in-a-row ttt on large board), Pente, Checkers, etc. It is probably easier to implement a game you are quite familiar with. You will not be judged solely on how well the program plays, but that will be a factor of course. Ideally I would like to be able to play against your program.

10. Symbolic Differentiation. The goal of this project is to take an equation (in infix notation) and a variable, and return an equation (in infix notation) that is the derivative of the given equation with respect to the supplied variable. The description is a bit long, but this is actually one of the easier projects, and is quite interesting. It consists of four parts:

(A) Infix to prefix translator. This will take expressions in infix notation like (5 * 3 + X) and return prefix expressions like (+ (* 5 3) X). This is easy, and can be taken directly from Winston & Horn, Chapter 32. See me for details if you don't have Winston & Horn.

(B) Symbolic Derivatives. This is the easiest of the four parts; a few rules and a recursive function to apply them is enough to be able to do derivatives of arbitrarily complicated expressions that only involve +, -, *, /, and expt.

In the following rules, note that u and v refer to functions of X, but a is simply a number. Feel free to look up and use any other rules such as for trig functions, uv, eu, etc. Even with only the above rules, you should be able to handle a large class of complicated derivatives that would be difficult to do by hand.

(C) Simplification. With these rules, although it is quite easy to get correct results, the answers will be much more complicated than necessary. That is, "blind" application of the rules will result in many entries that could be greatly simplified by combining terms, replacing X0 (X ¹ 0) with 1, etc. One approach is to make a whole bunch of special cases to try and catch this. For instance, the exponentiation rule could have a special case for X2, since the rule as written would give the derivative as (2X1)(1), rather than just 2X. This, however, would result in a plethora of special cases, and would still have some overly complicated results. A better approach is to do simplification as an entirely separate step.

You should look at the results of your differentiation routines to see what types of simplifications you will need. A couple you are certain to want are:

U0 ==> 1, U1 ==> U, U*V*0*W*X ==> 0, 1*X*2*X*3*X ==> 6X3, and simplifying any subexpression that only contains numbers, rather than variables (e.g. 23) to its actual value (e.g. 8). There are lots of possible cases, and you will not be able to get all of them. However, you should be able to get most of the obvious cases that occur when taking derivatives. This is actually the most difficult of the sections, and you should try to find a flexible/extensible way of specifying simplification rules. Norvig's rule-based approach may be overkill, but you can take a look.

(D) Prefix to Infix. As the final step, translate the simplified prefix expression back to parenthesized infix. If you are slightly more ambitious, you can use the order of operations to drop unnecessary parens. Once you have this, you can put all four parts together:
(Derivative '(2 * X ^ 3) 'X)==> (6 * (X ^ 2))

11. Any other project with approval. The main idea of the project is to do some nontrivial AI programming task. The projects listed are ones that I am familiar with and have implemented myself in the past. However, you are not limited to these. The more interesting and/or helpful the project is to you, the better. Projects do not necessarily even have to be on topics that were specifically covered in class, as long as they make significant use of AI programming techniques, are implemented in Lisp, and of course are done without significant assistance. Feel free to discuss potential ideas with me.


" 'Lisp is a programmable programming language.' There is more to Lisp than this, but the ability to bend Lisp to one's will is a large part of what distinguishes a Lisp expert from a novice. As well as writing their programs down toward the language, experienced Lisp programmers build the language up toward their programs."
Paul Graham, On Lisp: Advanced Techniques for Common Lisp, Prentice Hall, 1994.