TEACH TRANSSENT Last Modified A. Sloman, Feb 1984 Preliminary note: This TEACH file follows on from TEACH * PARSESENT in a series that starts with TEACH * ISASENT. It is helpful, but not essential, to have looked at these files. ALLPRESENT is used later on. If you have not met this procedure, then, HELP * ALLPRESENT will give you a short definition. A longer explanation of ALLPRESENT, and its generalisation to FOREVERY, can be found in Chapter Nine of Barrett, Ramsay and Sloman's book 'POP-11 A PRACTICAL LANGUAGE FOR ARTIFICIAL INTELLIGENCE'. In what follows, an account of how to translate nounphrases into lists of patterns is outlined. It is not a complete analysis of how to translate whole sentences. -- SCENARIO ------------------------------------------------------------ In this file a procedure called TRANSNP (for TRANSlate NounPhrase) is described. Given a noun phrase, TRANSNP returns a list of patterns that could be given to ALLPRESENT to find the 'referent' for the noun phrase. For example, given the noun phrase [a big man by a window] then TRANSNP will return something like: [[?x is a man] [?x is big] [?y is a window] [?x is by ?y]] Given the noun phrase: [a man that loves a woman that a dog bit] TRANSNP will return something like: [[?x is a man] [?y is a woman] [?z is a dog] [?z bit ?y] [?x loves ?y]] This list of patterns can then be used by ALLPRESENT, or FOREVERY to find items in the database which simultaneously satisfy all these patterns, with consistent values for the variables. -- BASIC METHOD -------------------------------------------------------- The routine TRANSNP will take two arguments: the noun phrase and a symbol (like X or Y) to stand for the 'referent' of the nounphrase. TRANSNP will, using ISA procedures break up the noun phrase into its constituents (like 'qualified nouns', which might be recognised by a procedure called ISAQNOUN). It will use appropriate TRANS procedures for these consituents and then collect together their output. The procedure will NOT work via a parse tree. This is for pedagogical reasons only. As implied in PARSESENT, using a parse tree would make the various TRANS procedures smaller and faster; in this handout we are interested in clarity only. -- SOME PRELIMINARY PROCEDURES ---------------------------------------- In what follows we shall need procedures for recognising various constituents of sentences, e.g. nouns, adjectives, etc. These can be defined as in TEACH PARSESENT and TEACH ISASENT. But in case you have not met them, here are some examples, from which you should be able to generalise. A procedure to recognise nouns: define isanoun(word) -> result; member(word, [cat dog man window computer thought]) -> result enddefine; You may alter the list of nouns to suit yourself. Procedures to recognise adjectives, verbs, prepositions can be defined similarly: isadj, isaverb, isaprep, etc. To recognise nouns qualified by any number of adjectives, you could define a procedure isaqnoun, as in teach isasent, or more compactly: define isaqnoun(list); vars x, y; list matches [?x:isanoun] or list matches [?x:isadj ??y:isaqnoun] enddefine; This assumes you have defined isanoun and isadj. We now have some 'recogniser' procedures, and we can use them to define translation procedures. -- A SIMPLE DEFINITION OF TRANSNP -------------------------------------- We start with an extremely simple definition of TRANSNP that allows noun phrase to be only of the form 'a noun', such as 'a man' or 'a dog'. This makes the code particularly simple. If we invoke TRANSNP with the list of words [A MAN] and the symbol X then it will return [[?X IS A MAN]], thus: define translate(list, symbol) -> result; vars x; if list matches [a ?x:isanoun] then [[? ^symbol is a ^x]] -> result else false -> result endif enddefine; The "?" is there in the result list, because we want to create a pattern which can later be used for searching the database. Be sure to leave the space between the '?' and the '^' in the part that goes [? ^symbol .... We have to do this in order that in the final result we get a question mark followed by the VALUE of SYMBOL. Notice that because we want to produce a LIST of patterns, as explained above, there are two square brackets in the RESULT. This looks a bit odd when there is only one element to the result but eventually there will be a whole list of elements. -- ALLOWING ADJECTIVES ------------------------------------------------- We can easily extend the simple TRANSNP shown above to allow for a single adjective, thus: define transnp(list, symbol) -> result; vars x, y; if list matches [a ?x:isanoun] then [[? ^symbol is a ^x]] -> result elseif list matches [a ?x:isadj ?y:isanoun] then [[? ^symbol is ^x] [? ^symbol is a ^y]] -> result else false -> result endif enddefine; We can test this (assuming we've defined isadj and isanoun): transnp([a happy man], "x") => ** [[? x is happy] [? x is a man]] -- EXERCISE ------------------------------------------------------------ Put that definition of TRANSNP into a file, with suitable definitions of ISANOUN and ISADJ, load them, and try the following examples (allowing for variations in your definition of ISADJ and ISANOUN): transnp([a man], "x") => ** [[?x is a man]] transnp([a man], "y") => ** [[?y is a man]] transnp([a big man], "x") => ** [[?x is big] [?x is a man]] transnp([a big man], "y") => ** [[?y is big] [?y is a man]] Notice that the symbol we give to TRANSNP is a word so we must write "X" and not [X] or just X. if you are unclear about this do the following: vars x; "y" -> x; x => [x] => "x" => [^x] => -- USING SUBSIDIARY PROCEDURES ----------------------------------------- Our TRANSNP program will shortly be getting complicated and we would be well advised to start breaking it up. We first define a procedure which translates qnouns (i.e. nouns qualified by adjectives), starting with a version able to cope with only one adjective. define transqnoun(list, symbol) -> result; vars x, y; if list matches [?x:isanoun] then [[? ^symbol is a ^x]] -> result elseif list matches [?x:isadj ?y:isanoun] then [[? ^symbol is ^x] [? ^symbol is a ^y]] -> result else false -> result endif enddefine; define transnp(list, symbol) -> result; vars x; if list matches [a ??x:isaqnoun] then transqnoun(x, symbol) -> result else false -> result endif enddefine; transnp([a happy man],"x") => ** [[? x is happy] [? x is a man]] but: transnp([a happy old man], "x") => ** So far, TRANSQNOUN will work only on QNOUNS with just zero or one adjectives. To make it work with any number of adjectives, we will have to make it recursive. -- RECURSIVE QNOUN ALLOWS FOR ANY NUMBER OF ADJECTIVES ----------------- We want to allow noun phrases like [a big happy old man] with several adjectives. Given such a list (and a symbol) transqnoun should produce a corresponding list of patterns. We don't know how many different adjectives there will be, so we need a strategy to cope with arbitrarily many adjectives. The basic approach taken here will be to treat the simple NOUN case as before but to break the translation of recursive QNOUNS into two stages, thus: define transqnoun(list, symbol) -> result; vars x, rest, temp; if list matches [?x:isanoun] then [[? ^symbol is a ^x]] -> result ;;; simple case elseif list matches [?x:isadj ??rest:isaqnoun] then transqnoun(rest, symbol) -> temp; ;;; recursion [[? ^symbol is ^x] ^^temp] -> result else false -> result endif enddefine; Notice that the line after the recursive line assumes that the value of temp is a list (i.e. a list of patterns), and then it adds another pattern to the front of the list. So the recursive call is assumed to produce the kind of result that we want transqnoun to produce. In other words, if the procedure works, it will work! Or to be more precise, if it works on the simpler cases (a smaller list given to the recursive call) then it will work on more complex cases. -- EXERCISE ------------------------------------------------------------ Transfer this recursive definition of TRANSQNOUN and the modified TRANSNP to your file. TRACE them both and then try the following examples: transqnoun([man], "x") => ** [[? x is a man]] transqnoun([big man], "x") => ** [[? x is big] [? x is a man]] transqnoun([big fat man], "x") => ** [[? x is big] [? x is fat] [? x is a man]] transnp([a big fat man], "y") => ** [[? y is big] [? y is fat] [? y is a man]] transnp([a big fat man], "x") => ** [[? x is big] [? x is fat] [? x is a man]] Try variations of your own, depending on which words you have defined to be adjectives or nouns. -- ADDING PREPOSITIONS ----------------------------------------------- Consider the noun phrase: [a man by a window] The translation of this is going to be something like: [[?x is a man] [?y is a window] [?x is by ?y]] Notice that we need TWO symbols for this translation X (for the man) and Y (for the window). There is a procedure in POP11 for 'generating' brand new never seen before words called GENSYM. We shall make use of GENSYM to translate noun phrases which have embedded prepositions. -- EXERCISE WITH GENSYM ------------------------------------------------ Try the following: gensym("cat") => What you should get back is a the word CAT with some number (probably one) appended, eg: ** cat1 Try the SAME command again. This time you get a different number appended, for example: gensym("cat") => ** cat2 gensym("cat") => ** cat3 GENSYM 'remembers' how many times it has been called with the same word as argument and always produces a brand new word. You can 'reset' its counter with a command like: 1 -> gensym("cat"); This resets the counter for the word CAT so that the next call of GENSYM("CAT") will produce CAT1. A subsequent use will produce CAT2 and so on until you reset the counter again (or restart the POP11 system in which case the counters will all be reset to one). Try GENSYM on the word X, thus: gensym("x") => gensym("x") => gensym("x") => 53 -> gensym("x"); gensym("x") => gensym("x") => 1 -> gensym("x"); gensym("x") => gensym("x"); -- BACK TO PREPOSITIONS ------------------------------------------------ To handle prepositions in noun phrases we must modify TRANSNP so that if it recognizes a preposition then it uses GENSYM to create a new symbol for the 'embedded' nounphrase, thus: define transnp(list, symbol) -> result; vars np1, prep, np2, temp1, temp2, newsymbol; if list matches [a ??np1:isaqnoun] then transqnoun(np1, symbol) -> result elseif list matches [??np1:isanp ?prep:isaprep ??np2:isanp] then gensym("x") -> newsymbol; transnp(np1, symbol) -> temp1; transnp(np2, newsymbol) -> temp2; [^^temp1 [? ^symbol is ^prep ? ^newsymbol] ^^temp2] -> result else false -> result endif enddefine -- EXERCISE ------------------------------------------------------------ Transfer this new definition of TRANSNP to your file. Be sure that there is a space between the various '?' and '^'. Make sure you have suitable definitions of ISAPREP, ISAQNOUN, and ISANP. (The structure of the latter would be very similar to the structure of TRANSNP). Try TRANSNP on the following noun phrases (suitably modified for your definitions of ISADJ, ISANOUN etc): transnp([a man by a window], "x") => transnp([a big man by a open window], "x") => -- EXERCISE ------------------------------------------------------------ Extend your program so that it can accept "the" or "an" instead of "a". One way would be to define a procedure ISADET which recognises 'determiners', i.e. words like a, the, each, some, an. Then the patterns used to define noun phrases, instead of just having the word "a" you could have something like ?first:isadet -- EXERCISE ------------------------------------------------------------ To demonstrate the effectiveness of this technique, set up a database with information about various objects, e.g. [bill is a man] [fred is a man] [bill is big] [fred is big] [bill is by w1] [w1 is a window] [w2 is a window] [w1 is open] etc. Then, see what ALLPRESENT does with the translation of a noun phrase: First reset GENSYM: 1 -> gensym("x"); Then declare as variables, the symbols which may be created by gensym: vars x1,x2,x3,x4,x5; vars trans; transnp([a big man by a open window], "x") ->trans; trans ==> ** [[? x is big] [? x is a man] [? x is by ? x1] [? x1 is open] [? x1 is a window]] allpresent(trans) => ** them ==> ** [[bill is big] [bill is a man] [bill is by w1] [w1 is open] [w1 is a window]] x=> ** bill x1 => ** w1 Write notes explaining what has happened with an example like this. -- EXERCISE ------------------------------------------------------------ Extend your program to accept relative clauses, ie: transnp([the man that loves the cat], "x") => -- FURTHER EXERCISES --------------------------------------------------- The following exercises are for the especially interested only. They may be skipped if so desired. -- EXERCISE ------------------------------------------------------------ Write a version of TRANSSENT that works only on YESNO questions, for example: transsent([does a man love a cat]) => ** [[?x is a man] [?y is a cat] [?x does love ?y]] Notice that TRANSSENT doesn't need to be given a symbol. -- EXERCISE ------------------------------------------------------------ Suppose that the DATABASE contained 'silly' assertions of the form: [name person1 steve] Using these database items we can translate nounphrases that are just names, for example: transnp([steve], "x") => ** [[name ?x steve]] transsent([does steve like a university]) => ** [[name ?x steve] [?y is a university] [?x does like ?y]] -- EXERCISE ------------------------------------------------------------ Suppose that there was a modified version of ALLPRESENT that took 'flags' saying whether a given pattern was supposed to be PRESENT or supposed to be ADDed. Let us call this new procedure PLANNER. This new procedure would be such that: planner( [[lookup ?x is a man] [lookup ?y is a woman] [lookup ?x does love ?y]]) is completely equivalent to allpresent( [[?x is a man] [?y is a woman] [?x does love ?y]]) This might be the meaning of the question: does a man love a woman However, we can use the PLANNER procedure to represent the meanings of ASSERTIONS as well, for example: transsent([steve does like tarina]) => ** [[lookup name ?x steve] [lookup name ?y tarina] [add ?x does like ?y]] Extend your program so that it accepts assertions as well as questions. For the moment, make the translation of a THE type nounphrase the same as that for an A type nounphrase. This will allow translations such as: transsent([the man does love the woman]) => ** [[lookup ?x is a man] [lookup ?y is a woman] [add ?x does love ?y]]) This is not quite accurate since it does not account for 'presupposition' errors (where there is, in fact, no mention of, say, any MAN in the DATABASE). ------------------------------------------------------------------------ Ask your course tutor to supply a definition of the PLANNER procedure if you want to test your programs. -- EXERCISE ------------------------------------------------------------ For this one you will need to know about FOREVERY. Speculate on the additions needed to the PLANNER procedure so that it could detect presupposition errors. Consider, also, HOWMANY type questions, eg: HOW MANY STUDENTS LIKE AI1 Think also of questions like: HOW MANY COURSES ARE LIKED BY AT LEAST TWO STUDENTS DOES EVERY STUDENT THAT STUDIES AI LIKE AI DO MORE STUDENTS LIKE AI THAN LIKE CT --- C.all/teach/transsent ---------------------------------------------- --- Copyright University of Sussex 1987. All rights reserved. ----------