WORCESTER POLYTECHNIC INSTITUTE
Computer Science Department

CS4341 ❏ Artificial Intelligence ❏ A'06

Mon, Tue, Thu, Fri - noon - AK 233
Prof. David C. Brown, Fuller Lab 131, (508) 831-5618, dcb at cs.wpi.edu

Version: Wed Aug 23 18:00:37 EDT 2006

PROJECT 1 - A Rule Interpreter

Write a simple rule interpreter and demonstrate its capabilities for a simple situation-recognition problem.

A Rule Interpreter (RI) is a kind of problem solver that uses problem-solving knowledge in the form of rules. A rule says what to do in a particular situation. The rule interpreter inspects these rules and solves a problem. The problem's initial conditions are requirements, a starting state, or symptoms, for example. That starting situation is given to the rule interpreter as a set of facts. The rules cause the rule interpreter to react to the facts to produce new facts, eventually leading to a set of facts that form an answer. We refer to the current set of facts as the state of the problem, or the "situation".

Each rule should be of the form "Situation recognized leads to Action executed, causing change in situation" or Situation-Action for short.

The key piece of the RI is a rule executor, or Inference Engine (IE), that acts on a set of rules. Using one rule at a time it will incrementally transform a description of some "initial" situation into a description of some "terminal" situation. The initial situation represents a problem to be solved, while the terminal situation is some form of answer.

The situation description is held in a Working Memory (WM). The description in the WM consists of a set of facts. These facts are simple descriptions of aspects of the problem or solution (see below for details).

The left hand side (LHS), or "situation recognition" side of a rule can be expressed as a boolean expression of arbitrary predicates. These predicates must be pre-defined (i.e., pre-loaded for the RI to use). The right hand side (RHS), or "action" side can be any sequence of actions. These actions too must be pre-defined. The actions act on the working memory. By "pre-defined" we mean that the predicate and action functions are given as input to the system, along with the rules, prior to any rule execution, and are not "built into" the IE. This makes the IE independent of any particular domain.

In the example below the action form (A-is v) means "set the value of the attribute A to v". So "(Plant-is Yes)" means "set the value of the attribute Plant to Yes" in the WM; i.e., It is a plant. The "Green?" predicate shown below is a short form for "Does the attribute Color have the value Green?". Similar interpretations apply for other predicates. These forms were chosen for improved readability. Other forms are possible.

Here is a sample set of rules. You do not have to use this form exactly, but you must follow it very closely.

    (IF ((Green?) AND (NOT (Heavy?))) THEN ((Type-is Grape)) )

    (IF ((Round?) AND ((Red?) OR (Green?))) THEN ((Type-is Apple)) )

    (IF (Red?) THEN ((Type-is Tomato)) )

    (IF (Tomato?) THEN ((Plant-is Yes) (Height-is Short)) )

    (IF (NOT (Ripe?)) THEN ((Color-is Green)) )

    (IF (CanCarry?) THEN ((Heavy-is No)) )

    (IF (TypeHasValue?) THEN ((Stop)) )

For the required demonstration of your system recognizing a situation, you should use a simple working memory consisting of a list of attributes and values. The size of the WM is fixed when the RI reads it. Only some of the values will be known at that time. Actions add values to the WM.

The use of "nil" below indicates that no value is known. Use another value to indicate "false" (or "No"). You can choose which values you want to use.

For example:

    (
    (Type nil )
    (Plant nil )
    (Vegetable nil )
    (Fruit nil )
    (Ripe No )
    (Color nil )
    (Shape Round )
    (Heavy nil )
    (Height nil )
    (Carry Yes )
    (Weight 2 )
    )

The control of rule selection and rule execution works as follows. All of the available rules are examined by the IE to see if one or more matches the situation described by the WM. A rule matches if its LHS evaluates to True. Predicates evaluate to True or to False by testing entries in the WM. If only one rule matches then the actions in the action list (RHS) of that rule are executed in sequence. This will change the WM.

If more than one rule matches, then that set of matching rules is known as the Conflict Set. The rule in the set that is most specific is the rule used. Specificity is up to you to define, but it probably has something to do with the length of the LHS. If there are two or more rules with the same specificity then the one chosen is the earlier one in the rule list. The idea is that more specific rules are more precise and should be favored over those which are less precise.

Matched rules are called "triggered" rules. The one chosen to be used is "fired".

For this RI, allow each rule to fire only once, by removing it from the current list of available rules after it has been used (i.e., after its action list has been used). After a rule fires, and it has been removed from the list, the IE cycles back to start again by examining all the available rules to see which match.

The system stops when no more rules match the current WM, when all rules have been used, or when a Stop action is executed. Stop is a predefined, domain-independent, interpreter-recognized action. This forces the IE to stop immediately.

Input the definitions of all predicates, definitions of all actions, the rules, and the initial state of the working memory: each from separate files. The RI program handles input, output, rule selection and rule execution. Note that the RI needs to produce appropriate output in demonstration mode so that we can see that it is working.

In some programming languages it is very be difficult to arrange for new predicates and actions to be input. If you think this is a problem, then discuss it with me. LISP or Scheme make this quite easy. Java and the Cs, for example, do not. It must be possible to define and use arbitrary predicate and action functions, so that the RI can work with different domains and can do different kind of problem-solving without needing to be changed. No rules, predicates, actions (apart from Stop) or WMs may be built into the RI.

Note that the rules given in this project do not include variables, so there is no binding to keep track of. You are not expected to implement the RETE approach that Winston describes.

Backward chaining

The description of the RI given above has been in terms of forward chaining, and can be considered to be what Winston calls a "reactive system": i.e., rules react to data in the WM, and work forwards using the rules from left to right.

In addition to implementing forward chaining you must implement a separate backward chaining IE. Much of what was described above also applies to using the RI in backward chaining mode (e.g., rules, the WM, actions and predicates all need to be available before the IE runs).

Backward chaining starts with an hypothesized goal (e.g., Type-is Grape) and uses the rules from right-to-left to try to show that it is in fact the case: i.e., the system is asked to assume that the fruit is a grape, and then looks to see whether the data provided supports that conjecture. In this case if ((Green?) AND (NOT Heavy?)) is true then the hypothesized goal can be shown. That requires looking in the WM first to see whether they are known. If not then the IE needs to look for those rules that can show both of those two subgoals: i.e., you look at the right-hand sides of all rules. In this case, one rule can produce (Color-is Green) while another can show (Heavy-is No). Note that a little predicate- and action-specific "translation" is required to make those connections: you get to decide how to do that. The more generic that is the better. Backward chaining continues with those two rules, and finds that both of their left-hand sides are true. This means that the hypothesized goal is true.

In general, backward chaining works from a goal, via a matching rule's right-hand side to subgoals obtained from its left-hand side, and keeps repeating that cycle. If more than one rule right-hand side is appropriate, then try all of them. If one route succeeds then the hypothesized goal has been shown. If none succeed then it has not.

It would probably be good to have some sort of switch/flag that can be set to select which IE to use. The RI will not be expected to mix its reasoning: i.e., for one WM it will either do all forward chaining or all backward chaining. Note that for backward chaining it needs to be given an hypothesized goal.

Demonstration

The situation recognition problem that you are to solve as a demonstration is up to you. A set of about 10-20 rules will be adequate. To show the capabilities of your system fully include several runs with different intial situations. You'll need to include forward and backward chaining examples. Please use the same rule-set for both directions. Think of this more as a test set than a real example of problem solving.

Note: you will be using this system in the next and the final project, and, as a consequence, you should make every effort to:

  • program it so that will be convenient;
  • complete it satisfactorily and on time.
Also note that THIS handout describes the project requirements, and a solution that looks like one online or in any AI text is not acceptable.

For the due date, see the Schedule. Projects must be completed by the start of class on the due date.

Submit

You must submit:

  • On paper and via turnin:
    • Brief, clear documentation that describes the design of your RI, including the overall architecture, any special algorithms used, and any special data structures used.
    • A description of how you've handled rule selection from the conflict set.
    • A description of how you've handled the predicate- and action-specific "translation".
    • A description of your rule language.
  • Via turnin only:
    • All the test cases in your demonstration with the corresponding output that shows that the RI is functioning correctly.
    • All the code (which must be well commented).

How to submit:

Please note:

  1. Clearly label all printed work with your name and with the ID number provided by the TA.
  2. Clearly label each file with a helpful name.
  3. ZIP your entire project directory. Make sure it only contains the files to be submitted. Use "zip" and not some other compression tool.
  4. Name the file as your user name + "-proj1.zip"   For example, "jefferson-proj1.zip"
  5. Submit the zipped project using web turnin.
  6. The project's turnin assignment name is "project1".
Please let us know if you still have problems.