This file is part of: AT&T Bell Laboratories and University of Pittsburgh CLASSIC Knowledge Representation System Tutorial Copyright AT&T and University of Pittsburgh 1994 ASG1.TXT INTRODUCTION ------------ This file contains your first CLASSIC assignment. With the help of the explanations and the code in this and other files you will build a knowledge base (KB) of wines and food. The more advanced lessons will show you how to use this knowledge base to support various reasoning tasks, such as how to select appropriate wines for different kinds of food or how to put together an appropriate meal. The goal of these assignments is to familiarize you with the CLASSIC knowledge representation system, and to provide an idea of the types of reasoning and knowledge representation that can be done using CLASSIC. CLASSIC is a fairly complex system that provides logical completions which, sometimes, are non-obvious. This first assignment is intended to be a hands-on tutorial introduction to CLASSIC. It is intentionally fairly mechanical; the purpose is to accustom you to the system and command language. You will practice using useful functions to modify and inspect the KB. In later assignments you will go on to explore some of the subtleties of the system. To better understand this first module of the tutorial, you should read the "Concept" section under "Description of CLASSIC Types" in the Classic Manual (section 2.1 in version 2.3 of the manual). All the functions mentioned in this file are documented in the appendix called "Classic Function and Variable Descriptions" (appendix D in version 2.2) in the manual. NOTATION -------- We will use the following conventions to make our definitions clearer: KB entities: concept names are in upper case, e.g., RED-WINE individual names are capitalized, e.g., Forman, Forman-Chardonnay role/attribute names are in lower case, e.g., food partition names (for disjoint primitives) are in lower case, e.g., type indices (for primitives and disjoint primitives) are also in lower case CLASSIC system: CLASSIC function names are in lower case, e.g., cl-define-concept CLASSIC language keywords are in upper case, e.g., AT-MOST LISP: LISP built-in functions and primitives are in lower case, e.g., defun LISP user functions are capitalized, e.g., No-Winery WHAT YOU NEED TO DO ------------------- The following text will tell you what to do at each point. You will be asked to type in some concept definitions and to use several CLASSIC functions to inspect the KB. In some cases you will be asked to write your own definitions. For some of these problems, we have provided answers, although you are encouraged to try to solve them yourself before giving up; for others you are on your own. Failing to solve a problem for which no solution has been provided will not critically impair continuing the work that follows the problem. You should follow the directions given by the text with respect to typing in definitions or function calls. You should also type in any lines of CLASSIC code that are prefixed by >>>. You should also follow directions with respect to the "dribble" files that log your work. Every time a written answer is required, remember to comment it using ";;", so that it will be easily recognizable in the dribble file. Finally, note that the assignment has been broken up into modules, so that you don't have to do it all in one session. Each module will start with a brief recapitulation of past work and instructions for files to load in order to "recover state". Your life will be easier if you try to get through a whole module before you take a break. WHAT TO TURN IN --------------- You will need to turn in evidence that you have gone through the assignment, i.e., log file(s) that record you going through the tutorial. In general: To record your work in a file use the Common Lisp function dribble. To start recording: (dribble "") To stop recording: (dribble) CAREFUL!! Use different filenames for different "dribble" files, or you will overwrite your previous work. LOADING FILES ------------- At several points in the assignment you will be told to load files. Some of these files contain CLASSIC definitions for building the KB. We have provided these because the KB is quite large. Other files can be used to "clean-up state", i.e., to remove mistakes you may have made in working through the assignment while insuring that all the needed definitions are loaded. These "recovery" files can also be used to resume working on the assignment if you don't get it done in one session. In most places below you will be using the function "lload" to load files, instead of "load". "lload" looks for files in the Helper or Recover sub-directories relative *topdir*, either the default top-level directory or the one that you have established. (See the assignment handout). When you see a filename like "/Helper/", or "/" substitute in the right name of the top-level directory you're using (that is the directory that you put the tutorial files into .. in fact the directory that this file is in). ------- Here we go. If you haven't started up classic-lisp yet, do so according to the instructions in the assignment handout. Make sure your work is being recorded in a file starting now. >>> (dribble "your-file1.log") First load the file "/Customize.lisp", i.e. >>> (load "/Customize.lisp") INITIALIZING CLASSIC -------------------- The function cl-startup initializes the KB. If you have just loaded CLASSIC, you don't need to call it, but if you have been working in CLASSIC for a while and are loading a new KB or you have made changes to the KB code and are reloading, you need to call it. The function cl-startup removes all concepts and individual definitions except THING, CLASSIC-THING, HOST-THING, and a few other built-in concepts. Since concepts cannot be changed within a KB, you will find that when you want to redefine a concept, you will need to re-initialize the KB as well. >>> (cl-startup) Turn off warning messages, if you want less feedback from the system. Default normally is t. >>> (cl-set-classic-warn-mode nil) DISJOINT-PRIMITIVE CONCEPTS --------------------------- We will now start building the first part of the hierarchy shown in the file "/classic-thing.ps" , i.e., the immediate children of CLASSIC-THING. There are four disjoint primitive concepts at the top level: WINE-PROPERTY, WINE-REGION, WINERY and CONSUMABLE-THING, all in the disjoint grouping "type". Such an organization of concepts is typical: A CLASSIC application will usually have primitives in the highest tiers of the hierarchy. NOTE 1: The picture in the file "classic-thing.ps" shows another child of CLASSIC-THING, CONCEPT. This is a predefined CLASSIC class, you can ignore it. NOTE 2: The pictures of the KB do not explicitly show disjoint groupings. You may want to add them by hand, using the notation shown in Figure 1, which is in the section called "Defined, Primitive, Disjoint Primitive and Temporary Concepts" in the Classic Manual (in version 2.2 it is in section 2.1.6). PRIMITIVES AND DISJOINT PRIMITIVES All concepts, including primitive, disjoint-primitive and non-primitive concepts, have at least one parent. If the parent is not specified in the definition, and the concept does not fit under a more specific concept, then it is classified under one of the built-in concepts, HOST-THING or CLASSIC-THING. A non-primitive concept definition specifies both the necessary and sufficient conditions that an individual must satisfy in order to be an instance of the concept. (Necessary conditions are the conditions that an instance of the concept must meet. Sufficient conditions are conditions that only instances of the concept meet; i.e., if an individual satisfies sufficient conditions, then it must be an instance of the concept.) Since you usually will be either unable or unwilling to specify all the sufficient conditions for very general concepts (e.g. WINE), or for concepts that are learned by example, you should be prepared to treat some concepts as primitives. On the other hand, CLASSIC obtains much of its reasoning power from well structured definitions. Whenever you can fully specify a concept by a definition, you should define it as a non-primitive concept. For example, the definition of the concept VEGETARIAN is someone who eats only vegetables. This definition fully specifies the concept. Thus, you should define VEGETARIAN as a non-primitive concept so that any individual that eats only vegetables will automatically get classified as a VEGETARIAN. See "Living with CLASSIC" for useful advice on this matter. A primitive concept definition (including a disjoint primitive concept definition) specifies the necessary but not the sufficient conditions that an individual must satisfy in order to be an instance of the concept. There may be additional information associated with primitive concepts, in the form of roles and restrictions. For instance, later we will introduce a disjoint primitive concept MEAL-COURSE with the roles food and drink, which must have fillers meeting certain conditions. If we know that Meal-Course-134 is a MEAL-COURSE, then we can infer, that Meal-Course-134 will have its food and drink roles filled by individuals that meet these conditions. (But, since a MEAL-COURSE is primitive, we cannot infer that an individual is a MEAL-COURSE if its food and drink role fillers meet the conditions since MEAL-COURSE is primitive.) The effect of making a concept P a primitive is that, even if a concept C has compatible restrictions or an individual Ind happens to satisfy P's restrictions, the system will not place C or Ind under P unless it is explicitly told to do so in the concept or individual definition. Now, using the function "cl-define-disjoint-primitive-concept" defined in the reference manual, create the disjoint primitive concepts WINE-PROPERTY, CONSUMABLE-THING, WINERY and WINE-REGION as children of CLASSIC-THING within a disjoint grouping called classic-thing-type. ---------------- The four disjoint primitive concepts that you have just defined create a partition or disjoint grouping (called "classic-thing-type") in the space of concepts under CLASSIC-THING. The four concepts are disjoint in the sense that any individual belonging to any one of them cannot also belong to any of the others. See the CLASSIC reference manual for other examples and more explanation of disjoint primitives and disjoint groupings. For example, CLASSIC-THING and HOST-THING are also defined as disjoint primitives under THING. Thus, CLASSIC will not allow you to create an individual that belongs to both a HOST-THING and CLASSIC-THING. The following statements try to violate the disjoint grouping. Try them and see what happens. They should fail to create anything, and return an error message. ; an incoherent concept >>> (cl-define-concept 'C '(AND WINE-PROPERTY WINE-REGION)) ; an inconsistent individual >>> (cl-create-ind 'Ind '(AND CLASSIC-THING HOST-THING)) TURNING OFF VERBOSE DEBUGGING INFORMATION So far, every time you have defined a concept, CLASSIC has printed out lots of information about its results. If you are loading large KB's this can get pretty verbose. CLASSIC has a "debug-mode" flag that you can set to different levels from 0 (no classification data) to 3 (lots of debugging information). The output you saw above results from the default. Let's turn it off and use other functions to find out how concepts get classified. >>> (cl-set-classic-debug-level 0) INSPECTING AND NAVIGATING THE KB -------------------------------- Now let's practice using some of the commands CLASSIC provides for inspecting the knowledge base. This will let you browse the information that has already been created for purposes of this exercise. Most of the CLASSIC functions that use concepts, individuals, and roles as arguments expect you to use a particular format for indicating these arguments. The only situation in which you can use a quoted symbol (e.g., 'C in the above concept definition) is if the argument is a . For , and arguments such as the concept person, the individual Joe and the role age need to use the following: @c{person} @i{Joe} @r{age} The short form @ will also work if there is no ambiguity, i.e., if you have not used the same name for a concept, individual, or role. (Note: CLASSIC itself does not distinguish between upper and lower case.) The functions cl-named-concept and cl-named-ind also take a symbol argument and return a concept or role respectively. Try typing: >>> (cl-named-concept 'WINERY) >>> @WINERY To print the contents of the concept WINE-REGION, type one of the following: >>> (cl-print-concept @c{WINE-REGION}) >>> (cl-print-concept @WINE-REGION) You can also use the more generic function cl-print-object: >>> (cl-print-object @c{WINE-REGION}) >>> (cl-print-object @WINE-REGION) All of these functions print the same information in the same format. Here is what the output should look like: *@c{WINE-REGION}* -> Derived Information: Parents: (@c{CLASSIC-THING}) Ancestors: (@c{THING}) @c{WINE-REGION} The output is easy to interpret. The `*' around the concept name indicates that this is a primitive or disjoint primitive CLASSIC-THING is the only parent (WINE-REGION was declared as a primitive under it) CLASSIC-THING is the most general concept in the domain of non-host objects: every individual created in the CLASSIC knowledge base will be a CLASSIC-THING. Thus, CLASSIC-THING will be an ancestor of every CLASSIC individual and concept. If you want to see more detailed information about an object, use the keyword parameters to the cl-print-concept function. If :told is non-nil, cl-print-concept will print all the information you have told CLASSIC about the object. Try typing: >>> (cl-print-concept @c{WINE-REGION} :told T) Remember how we defined @c{WINE-REGION}: (cl-define-disjoint-primitive-concept 'wine-region 'classic-thing 'classic-thing-type) Here's the output when :told is non-nil: *@c{WINE-REGION}* -> Derived Information: Parents: (@c{CLASSIC-THING}) Ancestors: (@c{THING}) Told Information: Member of decomposition(s): CLASSIC-THING-TYPE Parents: (@tc{CLASSIC-THING}) Here the Told Information includes information that this is a disjoint primitive on parent CLASSIC-THING. (Notice the @tc{} notation. This means that the object is a "told concept.") The Told Information also includes that it is in the disjoint grouping labeled classic-thing-type. (Recall that the disjoint grouping serves as a label of the decomposition of the parent concept to which the child concept belongs.) You can use almost anything as a name for a disjoint grouping. The names you choose are in most cases not particularly important, since they are only needed in cases when there are multiple decompositions of a parent concept; and they are often suppressed in the information that CLASSIC reports. But it is important to choose unique names for disjoint groupings. More complex concepts will have correspondingly more information associated with them. We will see some examples below. You can see what the hierarchy looks like so far, without printing out ALL the information associated with a concept or individual, by using the following CLASSIC functions, which are documented in the CLASSIC reference manual in the sections entitled "Retrieval Functions for Derived Concepts" and "Retrieval Functions for Derived Objects" (in version 2.2 of the manual they are section numbers D.6.2.4 and D.5.2). cl-concept-parents cl-concept-children cl-concept-ancestors cl-concept-descendants cl-bottom-concepts cl-top-concepts cl-primitives To get some practice in using the above functions to browse through a Classic hierarchy, try the following: Print the children of THING: (cl-concept-children @c{THING}) Starting from HOST-THING, use the above functions to get to INTEGER and verify that this concept has no children. Print the parents and the ancestors of WINE-REGION. Print the primitive ancestors of WINERY. See the section called "Retrieval Functions for both Concepts and Individuals" (in section D.5 in version 2.2) of the Classic Manual. PRACTICE WITH DISJOINT PRIMITIVES --------------------------------- Now let's define some more disjoint concepts in our KB. We'll work on the concepts below CONSUMABLE-THING in "/classic-thing.ps", that is, EDIBLE-THING and POTABLE-LIQUID. These should be disjoint concepts under CONSUMABLE-THING. Following the pattern we used above for CONSUMABLE-THING, WINE-PROPERTY, etc., define the two concepts EDIBLE-THING and POTABLE-LIQUID as disjoint primitives under CONSUMABLE-THING within a disjoint grouping called consumable-thing-type (remember, you will need to provide unique names for each disjoint grouping). You can look up the definitions of the primitive concepts defined up to now in "/Recover/after-consumable-thing.lisp" ---------------------------------------------------- We switch our attention now to subclasses of the primitive concept WINE-PROPERTY, as shown at the top of "/classic-thing.ps" and in more detail in "/wine-property.ps". The first thing we'll do is to create the four concepts WINE-COLOR, WINE-BODY, WINE-FLAVOR and WINE-SUGAR, under WINE-PROPERTY. These are not primitive concepts. They will be defined as enumerative concepts, i.e., concepts that are totally defined by their parents and a set of user-defined direct instances. Enumerative concepts are created using the ONE-OF restriction which is explained in the section entitled "ONE-OF" in the Classic Manual (section 2.1.4.3 in version 2.2 of the manual). We want each of the new concepts to be a WINE-PROPERTY whose remaining definition is simply to be one of the instances listed under it in "/wine-property.ps". So try to define the concept: 1. WINE-COLOR: Is a WINE-PROPERTY and is one of White, Rose, or Red. You might be surprised to see the result of using the cl-concept-instances command on WINE-COLOR. Use the command to see what the instances of WINE-COLOR are. You should have gotten NIL. This happens because the individuals White, Rose, and Red did not exist prior to the definition of WINE-COLOR. CLASSIC created these individuals, but since nothing else is specified about them, they are just classified as CLASSIC-THINGs. If WINE-COLOR was defined as "(ONE-OF White Rose Red)", and did not include the primitive concept WINE-PROPERTY in its definition, the individuals in the ONE-OF would be classified under it. However, it contains the primitive concept WINE-PROPERTY in its definition, so the WINE-COLOR individuals won't get classified under WINE-COLOR unless we specify that they are instances of WINE-COLOR. The command cl-populate is used to put them in their proper place, e.g. >>> (cl-populate 'WINE-COLOR '(White Rose Red)) Note that individuals are one thing that CLASSIC will automatically create if they haven't been mentioned before, unlike concepts and roles which have to be defined before they are used. CLASSIC can create an individual without knowing anything about it since information can be added to it at a later time. In contrast, role and concept definitions are not modifiable. Now create and populate the following concepts: WINE-BODY: Is a WINE-PROPERTY and is one of Light, Medium, or Full. WINE-FLAVOR: Is a WINE-PROPERTY and is one of Delicate, Moderate, or Strong WINE-SUGAR: Is a WINE-PROPERTY and is one of Sweet, Off-Dry, or Dry. You can use the KB navigation commands to make sure all the individuals are properly classified. Now that your knowledge base is populated with some individuals, you can browse the individuals, as well as the concepts. Some useful functions for retrieving information about individuals are: cl-ind-parents cl-ind-ancestors cl-concept-direct-instances cl-concept-instances Try the following: Print the direct instances of CLASSIC-THING. Print the instances of THING. Print the direct instances of WINE-PROPERTY. Print the direct instances of WINE-COLOR. Print the parents of the individual Sweet. Print the ancestors of the individual Strong. You can compare your definitions of the WINE-PROPERTY's subclasses with the ones in the file "/Recover/after-wine-properties.lisp". Load this file if you want to recover from errors you made in your definitions. DEFINITION OF CONCEPTS THROUGH ROLES and ATTRIBUTES ==================================================== Now that we have created the concept WINE-PROPERTY and its subconcepts, we can use them to define the concept WINE. We want to define WINE as a POTABLE-LIQUID with attributes color, body, flavor, sugar and grape. In order to do this, we need to create the necessary attributes first. Read the "Roles" section of the Classic Manual (section 2.5 in version 2.2) for more information about roles. For example, let's create the attribute 'color' >> (cl-define-primitive-role 'color :attribute t) Use the same construct to create the attributes 'body', 'flavor', 'sugar' and 'grape'. Now everything we need to define the concept WINE is in the KB. We want to define a WINE as a POTABLE-LIQUID that has: 1) exactly one 'color', which is a WINE-COLOR. 2) exactly one 'body', which is a WINE-BODY. 3) exactly one 'flavor', which is a WINE-FLAVOR. 4) exactly one 'sugar', which is a WINE-SUGAR. 5) exactly one 'grape', which is an EDIBLE-THING. Remember that Classic provides constructs for defining number restrictions on roles. For further information about this read the section called "Number (AT-LEAST, AT-MOST) Restrictions" (section 2.1.3.2 in version 2.2 of the manual). For example, to specify that WINE has exactly one color, you may combine an "AT-LEAST 1" and "AT-MOST 1" number restriction. But if a role is defined to be an attribute, an "AT-MOST 1" restriction is automatically established, while the default AT-LEAST restriction of an attribute and a regular role is set to 0. Therefore, if you have defined color, body, flavor, sugar and grape as attributes, only AT-LEAST number restrictions must be specified in the definition of the WINE concept. Also remember that in saying that there is one of something and it has a particular value, ALL (one) of those things has that value. Define the concept WINE now. To look at the newly defined concept, use >>> (cl-print-concept @wine :told t) What difference do you notice between the description of the Role Restrictions in the derived information and the one in the told information? Why does the difference exist? Comment out your answer using semi-colons (;;), so that it will be easily recognizable in your log file. -------------------- At this point, we want to add to our knowledge base the concept of MEAL-FOOD which is an EDIBLE-THING that can be served during a meal. Instances of MEAL-FOOD can be, for example, Pasta or Fries, while Flour is an EDIBLE-THING which is not a MEAL-FOOD, because raw flour will never be served during a meal. The purpose of the knowledge base that we are building is to plan meals in which the foods and wines that are served go together. Thus, the information about which wines go with a particular food has to be entered at some place in the knowledge base. We will use the role appropriate-drink for this purpose, which will later be used to attach appropriate drinks to each MEAL-FOOD in the knowledge base. Create the role appropriate-drink, and define the concept MEAL-FOOD so that every MEAL-FOOD: 1) is an EDIBLE-THING 2) has at least 1 appropriate-drink 3) any appropriate-drink is a WINE. If you are in trouble, you can find the definitions of WINE and MEAL-FOOD in "/Recover/after-meal-food.lisp" The remainder of the hierarchy under MEAL-FOOD is shown in "/edible-thing.ps" It's not very interesting, just many disjoint concept definitions. If you want to see some meal definitions, you may look at the following file, but PLEASE DO NOT load it: "/Helper/meal-foods.lisp". ROLE RESTRICTIONS ------------------- Now let's see how the restrictions defined on the roles of the WINE concept influence the creation of WINE individuals. Try to create an individual W1 that is a WINE and has sugar Dry. Notice what happens when you try to create a new individual W2 that is a WINE and has grape Semillon and Riesling. Remember that we defined the grape role to be an attribute, meaning that there could be only one grape per wine. Classic reminds you of this in the error message by creating a new individual (which you can examine) with the string "-*INCOHERENT*" in its name. The error message also states that it occurred along the "role-path (@r{GRAPE})", pointing out that the new value for the role does not work. You cannot access the error object by typing the name followed by "-*INCOHERENT*". You can access it by typing "(cl-error-object ')" as in "(cl-error-object 'Mary)". Create W2 again, this time choosing semillon as the only filler. Classic is not complaining about this second entry, and W2 is eventually created (make sure of this by printing W2, using cl-print-ind). Are you surprised about this? WINE has been defined as having at least one color, one flavor, one sugar and one body, but no fillers for these attributes have been assigned to W2. Why did Classic create W2 without complaining? If you have problems answering this question, watch how Classic complains when you try to type: >>> (cl-ind-close-role @W2 @color) Insert in your DRIBBLE file an explanation of what happened here. (Remember to comment out your explanation.) Type: (dribble) to close your log file now. --------------------------------------------------------------------------- END OF asg1.txt If you are going on, open the file "/asg2.txt"