Fall 2015 CSE 425 Lab 3: Resolving Horn Clauses in C++

Due by Friday December 4, 2015 at 11:59 pm (please note the new deadline)
(deadline for receipt of an e-mail with a .zip file containing your solution)
Final grade percentage: 20 percent

You again are allowed (and in fact encouraged) to work in teams of 2 or 3 people (but not more), though you also are allowed to work individually if you prefer.

Objective:

This lab is intended to continue to broaden your experience with programming language features and design approaches in C++, and with expression parsing, representation, matching, and transformation techniques (again using Horn clauses from logic programming as a specific example), including:

In this assignment, you will extend your code from the previous lab assignment to perform resolution and semantic evaluation of Horn clauses based on the logic programming, syntax, and semantic techniques we have discussed this semester.

Specifically, you will modify and extend your program from the previous lab assignment, so that it operates in an interactive mode receiving and executing commands repeatedly from the user, including a process command that will load well-formed Horn clauses from a file and display them to the user in a numbered list, an assert command that will read and parse a single Horn clause from the command line itself, and if it is well formed add it to the numbered list, up and down commands to allow the user to scroll through the numbered list of Horn clauses if it is longer than can be displayed on a single screen, a resolve command that takes the numbers of two Horn clauses in the list and (if it can) resolves them to produce one or more new Horn clauses and adds them to the numbered list, along with commands for setting and generating values for bound variables.

For this assignment, the program will run either without any command line arguments (e.g., simply as in lab3.exe) or with a single command line argument giving the number of Horn clauses to display at a time (e.g., as in lab3.exe 10), will store the number that was passed in the command line argument (or a default value suitable for the number of lines that can be displayed in a maximized console terminal window in the Windows lab environment while leaving room for messages and an input prompt, if no command line argument was given) and then will repeatedly read in one line of user input at a time, to execute each of the following commands as they are given:

Assignment:

    Part I - Readings and Resources:

  1. The following readings in the course text book may be useful as reference material while working on this lab assignment:

  2. The following on-line resource also may be useful while working on this lab assignment:

    Part II - Program Design and Implementation:

    Note: the details of this assignment are again intentionally somewhat under-specified, leaving you some room to choose what you think is the best way to implement them, as long as what you do is reasonable and you explain your design decisions in comments in the code and in your project report.

  3. As you implement your solution to this lab, please again feel free to declare and define new structs, classes, enumerations, functions, and other types that are helpful in the design and implementation of your lab solution. As you do that please maintain good modularity of your implementation at the file level as well as at the type level, e.g., giving each major abstraction like a class and its related functions and enumerations its own separate header (.h) file and source (.cpp) file.

    As in the previous lab assignments, it also is possible that further semantic checking (e.g., whether or not a particular file exists) will need to be performed as part of executing some commands.

  4. The process <filename> command should cause the program to check that the file given by <filename> can be opened for reading, and if not should display a message to that effect and then simply re-display the current list of Horn Clauses and prompt the user for the next command; otherwise the program should read in the contents of file <filename> as a sequence of whitespace delimited string tokens and process that sequence according to the following input grammar (an updated version of the input grammar from the previous assignment, with modifications to accomodate bound vs. unbound variables):

    hornclause -> LEFTPAREN head [body] RIGHTPAREN

    head -> predicate

    body -> LEFTPAREN predicate {predicate} RIGHTPAREN

    predicate -> LEFTPAREN name {symbol} RIGHTPAREN

    name -> LABEL

    symbol -> UNBOUND | NUMBER | BOUND

    where the UNBOUND token is any string containing only a single uppercase alphabetic character ('A' through 'Z'); the BOUND token is any string containing only a single lowercase alphabetic character ('a' through 'z'); and all other tokens are defined as they were in the previous assignment.

    Hint: you may want to consider extending your token hierarchy, e.g., with BOUND and UNBOUND tokens being subtypes of LABEL. As it processes the file, your program should again parse out the different elements of each well formed Horn clause expression according to the grammar above. In this assignment the program should fill in symbol table entries for bound variables (indicated by BOUND tokens) and predicates (you can either simply keep all unbound variables and numeric constants within the predicates, or if you would like you can keep -- and potentially print out -- separate collections of them; similarly you can keep the list of Horn clauses -- which should alias the predicates within the symbol table -- either within the symbol table or separately within a diferent distinct entity like a deductive database object).

    When a bound variable is introduced into the symbol table for the first time, the semantic attribute for its value should be set to 0.

  5. The assert command should scan and parse the rest of the line following the assert keyword as a sequence of whitespace delimited tokens, identifying any well formed Horn clauses and adding the predicates and bound variables from them to the symbol table, and then re-displaying the numbered list of Horn clauses (including those newly recognized Horn clauses), starting from the top of the list;

  6. Instead of trying to unify arbitrary predicates (like it did in the previous lab assignment), in this assignment when it loads Horn clauses from a file or from the rest of the line following an assert command, the program should instead treat its collection of Horn clauses as a deductive database, attempting unification only as part of resolution and only for the appropriate predicates (i.e., the head of a clause and a matching predicate in the body of another clause) within copies of the two Horn clauses whose numbers are indicated when the user issues a resolve command (described below).

  7. The up command should scroll up a full screen worth of Horn clauses if no number is given following the up keyword, or if a value is given should scroll upward that number of lines, but should stop at the top of the list of Horn clauses if it is reached in either case;

  8. The down command should scroll down a full screen worth of Horn clauses if no number is given following the down keyword, or if a value is given should scroll downward that number of lines, but should stop at the bottom of the list of Horn clauses if it is reached in either case;

  9. If the same number is given twice to the resolve command, or if the Horn clauses involved are identical, or if two numbers were not provided or if the head of the first clause cannot be unified with a predicate in the body of the second clause, the program should simply output a message to that effect after re-displaying the same screen of Horn clauses; otherwise the program should make a copy of the two Horn clauses, perform any substitutions needed to unify the head of the first clause with a predicate in the body of the second clause, and then replace the matched predicate in the copy of the second Horn clause with the (possibly updated after unification) body of the copy of the first clause, and then assert the resulting Horn clause into the deductive database (as as new Horn clause) and re-display the numbered list of Horn clauses (if the head of the first clause can be unified with more than one predicate in the body of the second clause, then the program should perform a separate resolution attempt, starting fresh with unmodified copies of the two original Horn clauses, for each such case);

    In addition, modify the unification semantics from the previous assignment so that for this assignment:

    1. renaming of unbound variables and substitution of numeric constants for both kinds of variables is scoped only to the new Horn clause that is produced through resolution, and does not modify the original Horn clauses whose copies were resolved to produce it;

    2. such renamings and substitutions should be calculated during unification of two predicates, but only should be applied if that unification is successful: if unification fails, the renamings and substitutions calculated during it should be discarded and that attempt at resolution should be abandoned (note that the Horn clauses may still be able to be resolved by a subsequent resolution attempt for that same pair of clauses, if the head of the first one can unify with a different predicate in the body of the second);

    3. successful substitutions (and for unbound variables, also renamings) are again remembered for each variable during (and only during) that particular resolution attempt, so that each occurrence of the same variable elsewhere in the same pair of Horn clause copies (even if in a different predicate in either of the Horn clauses) should have the same renamings and/or substitutions applied to it that were applied to its other occurrences during that same resolution attempt;

    4. a numeric constant (indicated by a NUMBER token) can be substituted for any bound variable (indicated by a BOUND token) whose value is the same as the constant's value;

    5. a numeric constant (indicated by a NUMBER token) can be substituted for any unbound variable (indicated by an UNBOUND token) that either is not subject to any previous substitutions during that resolution attempt, or for which the cumulative substitutions so far produce the same value as the constant's value;

    6. a bound variable may be substituted for any unbound variable that is not subject to any previous substitutions (other than renaming to another unbound variable) during that resolution attempt;

    7. an unbound variable may be substituted for any other unbound variable that is not subject to any substitutions (that is to say, an unbound variable may be renamed to another unbound variable unless the one being renamed already has had a bound variable or a numeric constant substituted for it);

    8. a (bound or unbound) variable may not be substituted for a numeric constant; and

    9. no other (bound or unbound) variable may be substituted for a bound variable.

    For example, given the following numbered list of Horn clauses:

    1: ( ( ascending 1 2 ) )

    2: ( ( ascending 2 3 ) )

    3: ( ( ascending x Z ) ( ( ascending x Y ) ( ascending Y Z ) ) )

    then if the command resolve 1 3 were given, if (and only if) bound variable x has value 1, the head of first Horn clause would unify successfully with both (1) the first predicate in the body of third Horn clause -- allowing that resolution attempt to produce a new fourth Horn clause, and (2) the second predicate in the body of third Horn clause -- allowing that resolution attempt to produce a new fifth Horn clause, giving (after both of the new clauses have been added to the deductive database):

    1: ( ( ascending 1 2 ) )

    2: ( ( ascending 2 3 ) )

    3: ( ( ascending x Z ) ( ( ascending x Y ) ( ascending Y Z ) ) )

    4: ( ( ascending 1 Z ) ( ( ascending 2 Z ) ) )

    5: ( ( ascending x 2 ) ( ( ascending x 1 ) ) )

    then if the command resolve 2 4 were given, the head of second Horn clause would unify with the (sole) predicate in the body of the fourth Horn clause, allowing resolution to produce a new (sixth) Horn clause (which is a fact, since it has only a head and no body) within the deductive database:

    1: ( ( ascending 1 2 ) )

    2: ( ( ascending 2 3 ) )

    3: ( ( ascending x Z ) ( ( ascending x Y ) ( ascending Y Z ) ) )

    4: ( ( ascending 1 Z ) ( ( ascending 2 Z ) ) )

    5: ( ( ascending x 2 ) ( ( ascending x 1 ) ) )

    6: ( ( ascending 1 3 ) )

  10. Whenever the program receives a randomize <variable> [<max>] command, it should first check whether <variable> is a syntactically valid bound variable (and if <max> is given whether it is a syntactically valid numeric (unsigned integer) constant corresponding to a NUMBER token): if so it should look up that variable in the symbol table and if it is not there should add it and then should set the bound variable's value to an unsigned integer (from the range 0 to <max> if <max> was given, or from the entire range of non-negative integers otherwise), obtained from a pseudorandom number sequence (e.g., seeded when the program starts by calling the C srand function with the result of calling the C time function with the value 0, and then calling the C rand function each time a new pseudorandom number is needed, or using the new C++11 library features available for generating pseudorandom number sequences).

  11. Whenever the program receives a set <variable> <value> command, it should first check whether <variable> is a syntactically valid bound variable: if so it should look up that variable in the symbol table and if it is not there should add it. If <variable> is a syntactically valid bound variable and <value> is a syntactically valid numeric (unsigned integer) constant (corresponding to a NUMBER token), the program then should set the variable's value to be that of the constant.

  12. Whenever the program receives a print command, it should print out a legible representation of the contents of the symbol table, including the predicates that are contained within the Horn clauses in the deductive database, and the names and values of the bound variables that appear within those predicates.
  13. To make sure that the features of your program are working correctly, run the executable program through a series of trials that test it with good coverage of cases involving both well formed and badly formed commands, with existing and missing files, and with input files containing different well formed and badly formed input Horn clauses, etc., in as many possible combinations as you can think of.

    In your project report please document which test cases you ran, and summarize what your program did and whether what you saw was or was not correct behavior (and why or why not) in each case.

  14. Prepare a .zip file that contains all of your project's C++ code files, input/output traces for the important cases you tested, and your project report. Send the .zip file containing your lab 3 solution as an e-mail attachment to the course e-mail account (cse425@seas.wustl.edu) by the submission deadline for this assignment. If you need to make changes to your lab solution you are welcome to send a new .zip file, and we will grade the latest one received prior to the deadline (according to the time stamp the server puts on the e-mail).

    IMPORTANT:please make sure that no .exe file is included in any .zip file you send, as the WUSTL e-mail servers will block delivery of email with a .zip attachment that includes a .exe file. Please also make sure to send a .zip file (not a .7z file or other zip format) when you send your solutions.

    Part III - Extra Credit (1 to 5 percent of assignment's value depending on quality and completeness):

  15. Modify your program so that its commands that deal with Horn clauses can handle Horn clauses whose heads are conjunctions of well formed predicates, as well as Horn clauses whose heads are single predicates. Design an input file of Horn clauses with which to test that additional feature, and submit a copy of that input file along with your lab solution.

    Please add an extra credit section to your project report documenting your design and implementation of that additional capability. In that same section, please show examples of input and output from the different cases that you tested in order to validate that it is working correctly.

    Please submit both the required and extra credit portions within the same code base, along with your project report.


Posted 8:45am Monday October 26 2015 by
Chris Gill