File Information
File: 05-lr/acl_arc_1_sum/cleansed_text/xml_by_section/metho/95/p95-1012_metho.xml
Size: 13,436 bytes
Last Modified: 2025-10-06 14:14:02
<?xml version="1.0" standalone="yes"?> <Paper uid="P95-1012"> <Title>Compiling HPSG type constraints into definite clause programs</Title> <Section position="4" start_page="85" end_page="86" type="metho"> <SectionTitle> 3 Modelling HPSGII theories on a </SectionTitle> <Paragraph position="0"> relational level: a simple picture There are three characteristics of HPSGII theories which we need to model on the relational level: one needs to be able to 1. express constraints on any kind of object, 2. use the hierarchical structure of the type hierarchy to organize the constraints, and 3. check any structure for consistency with the theory.</Paragraph> <Paragraph position="1"> A straightforward encoding is achieved by expressing each of these three aspects in a set of relations. Let us illustrate this idea with a simple example. Assume the signature given in figure 3 and the HPSGII 2 For the logical foundations of relational extensions of arbitrary constraint languages see (HShfeld and Smolka, 1988). style theory of figure 4.</Paragraph> <Paragraph position="3"> First, we define a relation to express the constraints immediately specified for a type on the argument of the relation:</Paragraph> <Paragraph position="5"> For every type, the relation specifies its only argument to bear the type information and the consequents of the type definition for that type. Note that the simple type assignment \[G a\] leads to a call to the relation atvp~ imposing all constraints for type a, which is defined below.</Paragraph> <Paragraph position="6"> Second, a relation is needed to capture the hierarchical organization of constraints:</Paragraph> <Paragraph position="8"> Each hierarchy relation of a type references the constraint relation and makes sure that the constraints below one of the subtypes are obeyed.</Paragraph> <Paragraph position="9"> Finally, a relation is defined to collect all constraints on a type:</Paragraph> <Paragraph position="11"> aA disjunction of the immediate subtypes of T.</Paragraph> <Paragraph position="12"> Compared to the hierarchy relation of a type which collects all constraints on the type and its subtypes, the last kind of relation additionally references those constraints which are inherited from a supertype.</Paragraph> <Paragraph position="13"> Thus, this is the relation that needs to be queried to check for grammaticality.</Paragraph> <Paragraph position="14"> Even though the simple picture with its tripartite definition for each type yields perspicuous code, it falls short in several respects. The last two kinds of relations (reltype and relhier) just perform inheritance of constraints. Doing this at run-time is slow, and additionally there are problems with multiple inheritance.</Paragraph> <Paragraph position="15"> A further problem of the encoding is that the value of an appropriate feature which is not mentioned in any type definition may nonetheless be implicitly constrained, since the type of its value is constrained. Consider for example the standard HPSG encoding of list structures. This usually involves a type he_list with appropriate features HD and TL, where under HD we encode an element of the list, and under TL the tail of the list. Normally, there will be no extra constraints on ne_list. But in our setup we clearly need a definite clause</Paragraph> <Paragraph position="17"> since the value of the feature HD may be of a type which is constrained by the grammar. Consequently, since he_list is a subtype of list, the value of TL needs to be constrained as well.</Paragraph> </Section> <Section position="5" start_page="86" end_page="88" type="metho"> <SectionTitle> 4 Compiling HPSG type constraints </SectionTitle> <Paragraph position="0"> into definite clauses After this intuitive introduction to the problem, we will now show how to automatically generate definite clause programs from a set of type definitions, in a way that avoids the problems mentioned for the simple picture.</Paragraph> <Section position="1" start_page="86" end_page="87" type="sub_section"> <SectionTitle> 4.1 The algorithm </SectionTitle> <Paragraph position="0"> Before we can look at the actual compilation procedure, we need some terminology.</Paragraph> <Paragraph position="1"> A defined type is a type that occurs as antecedent of an implicational constraint in the grammar. Definition (constrained type) A constrained type is a type that interacts with a defined type.</Paragraph> <Paragraph position="2"> Whenever we encounter a structure of a constrained type, we need to check that the structure conforms to the constraint on that type. As mentioned in section 2.1, due to the closed world interpretation of type hierarchies, we know that every object in the denotation of a non-minimal type t also has to obey the constraints on one of the minimal subtypes of t. Thus, if a type t has a subtype t' in common with a defined type d, then t ~ is a constrained type (by virtue of being a subtype of d) and t is a constrained type (because it subsumes t').</Paragraph> <Paragraph position="3"> Definition (hiding type) The set of hiding types is the smallest set s.t. if t is not a constrained type and subsumes a type to that has a feature f appropriate s.t. approp(to,f) is a constrained type or a hiding type, then t is a hiding type. The type ne_list that we saw above is a hiding type. Definition (hiding feature) If t is a constrained or hiding type, then f is a hiding feature on t iff approp(t,f) is a constrained or hiding type.</Paragraph> <Paragraph position="4"> Definition (simple type) A simple type is a type that is neither a constrained nor a hiding type.</Paragraph> <Paragraph position="5"> When we see a structure of a simple type, we don't need to apply any constraints, neither on the top node nor on any substructure.</Paragraph> <Paragraph position="6"> Partitioning the types in this manner helps us to construct definite clause programs for type constraint grammars. For each type, we compute a unary relation that we just give the same name as the type. Since we assume a closed world interpretation of the type hierarchy, we really only need to compute proper definitions for minimal types. The body of a definition for a non-minimal type is just a disjunction of the relations defining the minimal subtypes of the non-minimal type.</Paragraph> <Paragraph position="7"> When we want to compute the defining clause for a minimal type, we first of all check what sort of type it is. For each simple type, we just introduce a unit clause whose argument is just the type. For a constrained type t, first of all we have to perform constraint inheritance from all types that subsume t. Then we transform that constraint to some internal representation, usually a feature structure (FS). We now have a schematic defining clause of the form t(FS) :- ?.</Paragraph> <Paragraph position="8"> Next, we compute the missing right-hand side (RHS) with the following algorithm.</Paragraph> <Paragraph position="9"> 1. Compute HF, the set of hiding features on the type of the current node, then insert these features with appropriate types in the structure</Paragraph> <Paragraph position="11"> (FS) if they're not already there. For each node under a feature in HF, apply step 2.</Paragraph> <Paragraph position="12"> 2. Let t be the type on the current node and X its tag (a variable).</Paragraph> <Paragraph position="13"> (a) If t is a constrained type, enter t(X) into RHS (if it's not already there).</Paragraph> <Paragraph position="14"> (b) Elseif t is a hiding type, then check if its hiding features and the hiding features of all its hiding subtypes are identical. If they are identical, then proceed as in step 1. If not, enter t(X) into RHS.</Paragraph> <Paragraph position="15"> (c) Else (t is a simple type) do nothing at all. For hiding types, we do exactly the same thing, except that we don't have any structure to begin with. But this is no problem, since the hiding features get introduced anyway.</Paragraph> </Section> <Section position="2" start_page="87" end_page="88" type="sub_section"> <SectionTitle> 4.2 An example </SectionTitle> <Paragraph position="0"> A formal proof of correctness of this compiler is given in (GStz, 1995) - here, we will try to show by example how it works. Our example is an encodin~ of a definite relation in a type constraint setup2 append_c appends an arbitrary list onto a list of constants. null We will stick to an AVM style notation for our examples, the actual program uses a standard feature term syntax. List are abbreviated in the standard HPSG manner, using angled brackets.</Paragraph> <Paragraph position="1"> T} and the set of hiding types is {list, ne_list}. Converting the first disjunct of append_c into a feature structure to start our compilation, we get something</Paragraph> </Section> </Section> <Section position="6" start_page="88" end_page="89" type="metho"> <SectionTitle> .GOALS e_list.I </SectionTitle> <Paragraph position="0"> :-?.</Paragraph> <Paragraph position="1"> Since the values of the features of append_c are of type list, a hiding type, those features are hiding features and need to be considered. Yet looking at node \[-i7, the algorithm finds e_list, a simple type, and does nothing. Similarly with node \[~\]. On node ~\], we find the hiding type list. Its one hiding subtype, ne_list, has different hiding features (list has no features appropriate at all). Therefore, we have to enter this node into the RHS. Since the same node appears under both ARG1 and ARG2, we're done and have \[</Paragraph> <Paragraph position="3"> which is exactly what we want. It means that a structure of type append_c is well-formed if it unifies with the argument of the head of the above clause and whatever is under ARG2 (and AR.G3) is a well-formed list. Now for the recursive disjunct, we start Therefore we don't enter that node in the RHS, but proceed to look at its features. Node \[\] bears a simple type and we do nothing, but node \[\] is again a list and needs to be entered into the RHS. Similarly with nodes \[\] and \['~. append_c on node \[\] is a constrained type and \[\] also has to go onto the RHS. The final result then is list(~), list(\[~\]), list(\[~\]), append_c(~\]). This is almost what we want, but not quite. Consider node ~\]. Clearly it needs to be checked, but what about nodes ~\], \[\] and E\]? They are all embedded under node \[\] which is being checked anyway, so listing them here in the RHS is entirely redundant. In general, if a node is listed in the RHS, then no other node below it needs to be there as well. Thus, our result should really be</Paragraph> <Paragraph position="5"> appendoc(\[~\]).</Paragraph> <Paragraph position="6"> Our implementation of the compiler does in fact perform this pruning as an integrated part of the compilation, not as an additional step.</Paragraph> <Paragraph position="7"> It should be pointed out that this compilation result is quite a dramatic improvement on more naive on-line approaches to ttPSG processing. By reasoning with the different kinds of types, we can drastically reduce the number of goals that need to be checked on-line. Another way of viewing this would be to see the actual compilation step as being much simpler (just check every possible feature) and to subsequently apply program transformation techniques (some sophisticated form of partial evaluation). We believe that this view would not simplify the overall picture, however.</Paragraph> <Section position="1" start_page="89" end_page="89" type="sub_section"> <SectionTitle> 4.3 Implementation and Extensions </SectionTitle> <Paragraph position="0"> The compiler as described in the last section has been fully implemented under Quintus Prolog. Our interpreter at the moment is a simple left to right backtracking interpreter. The only extension is to keep a list of all the nodes that have already been visited to keep the same computation from being repeated. This is necessary since although we avoid redundancies as shown in the last example, there are still cases where the same node gets checked more than once.</Paragraph> <Paragraph position="1"> This simple extension also allows us to process cyclic queries. The following query is allowed by our system. me_list ~\]</Paragraph> </Section> </Section> <Section position="7" start_page="89" end_page="89" type="metho"> <SectionTitle> Query> \[~\] \[THD </SectionTitle> <Paragraph position="0"> An interpreter without the above-mentioned extension would not terminate on this query.</Paragraph> <Paragraph position="1"> The computationally oriented reader will now wonder how we expect to deal with non-termination anyway. At the moment, we allow the user to specify minimal control information.</Paragraph> <Paragraph position="2"> * The user can specify an ordering on type expansion. E.g., if the type hierarchy contains a type sign with subtypes word and phrase, the user may specify that word should always be tried before phrase.</Paragraph> <Paragraph position="3"> * The user can specify an ordering on feature expansion. E.g., HD should always be expanded before TL in a given structure.</Paragraph> <Paragraph position="4"> Since this information is local to any given structure, the interpreter does not need to know about it, and the control information is interpreted as compiler directives.</Paragraph> </Section> class="xml-element"></Paper>