Esempio n. 1
0
    def _applyConstraints(self, parent, child):
        """ Constraint step:
		Given a satisfied state ``child'' and the updated state ``parent,'' compositional constraints
		are applied in the form of unification between the semantic heads of the two states.
		A successful unification implies the two states are compatible given the compositional 
		constraints.
		In which case, the semantic bodies are merged and updated to reflect the unification.
		Lastly, the string of the ``parent'' is updated.

		Because unification is a tricky process, care needs to be taken to ensure that it is properly
		performed.
		Variables that have the same name are assumed to be the same, but because the grammar generates
		feature structures for compositional constraints using a small set of variable names, often
		variables can have the same name despite their being independent entities.
		One workaround is to rename the variables in one of the semantic heads before unification.
		It is important that the renamed variables are updated in the semantic bodies as well.
	"""
        if LWFG.is_terminal(child.prod.lhs()):	# child is a special lexical state, and thus contains no semantic info
            return [True, parent]		# skip

	###################################################
	# Unification of Semantic Heads
	##################################################

	# The compositional constraints are represented in such a way that there a separate set of constraints for each
	# term in the production rule.
	# Each set of constraints is a separate feature structure.
	# The set of constraints for the LHS are indexed with a feature identifier: `h'
	# The set of constraints for the ith term in the RHS are indexed with a feature indentifier: `hi'
	#
	# Thus, when unification is performed, the constraints of the LHS of the child state (indexed by `h') need
	# to be retrieved.
	# Also, because NLTK unifies embedded feature structures only when their feature identifiers are the same,
	# we need to have the feature id of the retrieved set of constraints to match the corresponding feature id
	# in the parent state.
	# Again, that would be `hi' where $i$ is the index of the child's LHS in the parent's RHS.
        hidx = 'h'+str(parent.dotIdx)
        childHead = FS()  # make a new Feature Structure to get around the copy-by-ref issues
        childHead[hidx] = child.head['h']
    
        parentHead = parent.head
        
        # Rename variables in childHead to avoid confusion
        # step 0: check if child.body and parent.body share variable names, change any that are shared
        renamedVarMap1 = {}
        usedVars = []
        if parent.body:
            pBodVars = parent.body.variables()
            cBodVars = child.body.variables()
            for v in cBodVars:
                if v in pBodVars: # change v
                    nv = self._newVarName(v)
                    while nv in pBodVars+cBodVars:
                        nv = self._newVarName(nv)
                    usedVars.append(Variable(nv))
                    renamedVarMap1[Variable(v)] = Variable(nv)
                else: # not shared by parent
                    usedVars.append(Variable(v))
            for v in pBodVars:
                usedVars.append(Variable(v))
        else:
            for v in child.body.variables():
                usedVars.append(Variable(v))
                    

        # step 1: find used variables from parent's semantic head  
        usedVars += list(parentHead.variables())

        # step 2: rename variables in child's semantic head
        renamedVarMap2 = {}
        childHead = childHead.rename_variables(used_vars=usedVars, new_vars=renamedVarMap2)

	# check if features align with each other
        childFeats = set(childHead[hidx].keys())
        parentFeats = set(parentHead[hidx].keys())
        if not (parentFeats <= childFeats): # True if the relevant set of features of the parent state
					    # are **not** a subset of those of the child state.
            return [False, parent]	    # If True, the states are incompatible.
        
	# perform unification
        bindings = {}
        parentHead = parentHead.unify(childHead, bindings)
        if not parentHead: # failed to unify
            return [False, parent] # the states are incompatible

        # update pState's rule
        parent.head = parentHead

	###################################################
	# Updating Semantic Bodies
	##################################################
        childBody = LWFG.OntoSeR(str(child.body)) # create new sem body to get around copy-by-ref issues

        # The next part is a bit confusing and needs spelling out.
        # We want to change variables names in the child's semantic body that are shared with
        # the parent's semantic body.
        # However, we can't just do it willy-nilly, we need to ensure that only those variable names
        # that are not linked with the parent's body are changed.
        # For example, in "the smart girl" -- when "n -> det n" is completed, the body corresponding to "the"
        # is linked with the body corresponding to "smart girl".
        # If we change variables names without consideration, that link is lost.
        #
        # To maintain the link, a roundabout method is employed.
        # We first look at all renamed var names in "renamedVarMap2" -- which results from changing vars in
        # the child's semantic head.
        # We then look at all renamed names and see whether they were involved in unification with the parent's
        # head.
        # We can do that by going through the unification "bindings" and checking to see if the renamed names
        # are keys.
        # If they were involved in unification, we check to see if they were bound to a variable in the parent's
        # head that we were about to rename.
        # We can do that by looking to see if the bound variable is a key in "renamedVarMap1" -- which we got
        # from renaming variable names in the child's body that were present in the parent's body.

        for var in renamedVarMap2.keys():
            if renamedVarMap2[var] in bindings.keys() and bindings[renamedVarMap2[var]] in renamedVarMap1.keys():
                renamedVarMap1.pop(bindings[renamedVarMap2[var]])

        childBody.substituteBindings(renamedVarMap2)            
        childBody.substituteBindings(renamedVarMap1)

        if not parent.body:
##            print parent, child
##            print parent.body, child.body
##            print renamedVarMap1
##            print renamedVarMap2
##            print bindings
##            print " "
            parentBody = childBody
        else:
            
            parentBody = LWFG.OntoSeR(str(parent.body) + ',' + str(childBody))
        parentBody.substituteBindings(bindings)
        parent.body = parentBody

	###################################################
	# Updating Strings
	##################################################
        if not parent.string:
            parentString = child.string
        else:
            parentString = parent.string + ' ' + child.string
        parent.string = parentString
        
        return [True, parent]