示例#1
0
    def update_semantics(self, semantics):
        parent = None
        node, index = semantics, self.index
        while index > 0:
            if not isinstance(node, l.LambdaExpression):
                raise ValueError(
                    "semantics provided do not have sufficient arity to extract argument at position %i"
                    % self.index)

            parent = node
            node = node.term

        # Convert target variable to a uniquely named function variable.
        target_variable = node.variable
        new_target_var = make_unique_variable(node.term, "function")

        # Create an application expression applying the raised target var to some
        # extracted argument.
        extracted_arg = make_unique_variable(semantics,
                                             allow_clash=[target_variable])
        application_expr = l.ApplicationExpression(
            l.FunctionVariableExpression(new_target_var),
            l.IndividualVariableExpression(extracted_arg))

        node.term = node.term.replace(target_variable, application_expr)
        node.variable = new_target_var

        if parent is not None:
            parent.term = node

        return l.LambdaExpression(extracted_arg, semantics)
示例#2
0
    def applyto(self, arg):
        """ self = (\\x.(walk x), (subj -o f))
            arg  = (john        ,  subj)
            returns ((walk john),          f)
        """

        if self.indices.intersection(
                arg.indices):  # if the sets are NOT disjoint
            raise linearlogic.LinearLogicApplicationError, '%s applied to %s.  Indices are not disjoint.' % (
                self, arg)
        else:  # if the sets ARE disjoint
            return_indices = self.indices.union(arg.indices)

        try:
            return_glue = linearlogic.ApplicationExpression(
                self.glue, arg.glue, arg.indices)
        except linearlogic.LinearLogicApplicationError:
            raise linearlogic.LinearLogicApplicationError, '%s applied to %s' % (
                self, arg)

        arg_meaning_abstracted = arg.meaning
        if return_indices:
            for dep in self.glue.simplify(
            ).first.second.dependencies[::
                                        -1]:  # if self.glue is (A -o B), dep is in A.dependencies
                arg_meaning_abstracted = logic.LambdaExpression(
                    logic.Variable('v%s' % dep), arg_meaning_abstracted)
        return_meaning = logic.ApplicationExpression(self.meaning,
                                                     arg_meaning_abstracted)

        return self.__class__(return_meaning, return_glue, return_indices)
示例#3
0
 def lambda_abstract(self, other):
     assert isinstance(other, GlueFormula)
     assert isinstance(other.meaning, logic.VariableExpression)
     return self.__class__(logic.LambdaExpression(other.meaning.variable, self.meaning), \
                           linearlogic.ApplicationExpression(
                               linearlogic.ApplicationExpression(
                                   linearlogic.Operator(linearlogic.Parser.IMPLIES),
                                   other.glue),
                               self.glue
                               )
                           )
示例#4
0
def extract_lambda(expr):
    """
  Extract `LambdaExpression` arguments to the top of a semantic form.
  This makes them compatible with the CCG parsing setup, which needs top-level
  lambdas in order to perform function application during parsing.
  """
    variables = []

    def process_lambda(lambda_expr):
        # Create a new unique variable and substitute.
        unique = l.unique_variable()
        unique.type = lambda_expr.variable.type
        new_expr = lambda_expr.term.replace(
            lambda_expr.variable, l.IndividualVariableExpression(unique))
        return unique, new_expr

    # Traverse the LF and replace lambda expressions wherever necessary.
    def inner(node):
        if isinstance(node, l.ApplicationExpression):
            new_args = []

            for arg in node.args:
                if isinstance(arg, l.LambdaExpression):
                    new_var, new_arg = process_lambda(arg)

                    variables.append(new_var)
                    new_args.append(new_arg)
                else:
                    new_args.append(inner(arg))

            return make_application(node.pred.variable.name, new_args)
        else:
            return node

    expr = inner(expr)
    wrappings = []

    for variable_ordering in itertools.permutations(variables):
        wrapping = expr
        for variable in variable_ordering:
            wrapping = l.LambdaExpression(variable, wrapping)

        wrappings.append(wrapping.normalize())

    return wrappings
示例#5
0
    def _iter_expressions_inner(self,
                                max_depth,
                                bound_vars,
                                type_request=None,
                                function_weights=None):
        """
    Enumerate all legal expressions.

    Arguments:
      max_depth: Maximum tree depth to traverse.
      bound_vars: Bound variables (and their types) in the parent context. The
        returned expressions may reference these variables. List of `(name,
        type)` tuples.
      type_request: Optional requested type of the expression. This helps
        greatly restrict the space of enumerations when the type system is
        strong.
      function_weights: Override for function weights to determine the order in
        which we consider proposing function application expressions.
    """
        if max_depth == 0:
            return
        elif max_depth == 1 and not bound_vars:
            # require some bound variables to generate a valid lexical entry
            # semantics
            return

        for expr_type in self.EXPR_TYPES:
            if expr_type == l.ApplicationExpression:
                # Loop over functions according to their weights.
                # from pprint import pprint
                # pprint(sorted([(fn.weight, fn.name) for fn in self.functions], key=lambda x: x[0]))
                fn_weight_key = (lambda fn: function_weights[fn.name]) if function_weights is not None \
                                else (lambda fn: fn.weight)
                fns_sorted = sorted(self.functions_dict.values(),
                                    key=fn_weight_key,
                                    reverse=True)

                if max_depth > 1:
                    for fn in fns_sorted:
                        # If there is a present type request, only consider functions with
                        # the correct return type.
                        # print("\t" * (6 - max_depth), fn.name, fn.return_type, " // request: ", type_request, bound_vars)
                        if type_request is not None and fn.return_type != type_request:
                            continue

                        # Special case: yield fast event queries without recursion.
                        if fn.arity == 1 and fn.arg_types[
                                0] == self.types.EVENT_TYPE:
                            yield make_application(
                                fn.name,
                                (l.ConstantExpression(l.Variable("e")), ))
                        elif fn.arity == 0:
                            # 0-arity functions are represented in the logic as
                            # `ConstantExpression`s.
                            # print("\t" * (6 - max_depth + 1), "yielding const ", fn.name)
                            yield l.ConstantExpression(l.Variable(fn.name))
                        else:
                            # print("\t" * (6 - max_depth), fn, fn.arg_types)
                            sub_args = []
                            for i, arg_type_request in enumerate(fn.arg_types):
                                # print("\t" * (6 - max_depth + 1), "ARGUMENT %i %s (max_depth %i)" % (i, arg_type_request, max_depth - 1))
                                sub_args.append(
                                    self._iter_expressions_inner(
                                        max_depth=max_depth - 1,
                                        bound_vars=bound_vars,
                                        type_request=arg_type_request,
                                        function_weights=function_weights))

                            for arg_combs in itertools.product(*sub_args):
                                candidate = make_application(
                                    fn.name, arg_combs)
                                valid = self._valid_application_expr(candidate)
                                # print("\t" * (6 - max_depth + 1), "valid %s? %s" % (candidate, valid))
                                if valid:
                                    yield candidate
            elif expr_type == l.LambdaExpression and max_depth > 1:
                for bound_var_type in self.observed_argument_types:
                    bound_var = next_bound_var(bound_vars, bound_var_type)
                    subexpr_bound_vars = bound_vars + (bound_var, )

                    subexpr_type_requests = []
                    if type_request is None:
                        subexpr_type_requests = [None]
                    else:
                        # Build new type requests using the flat structure.
                        type_request_flat = type_request.flat

                        subexpr_type_requests.append(type_request_flat)

                        # Basic case: the variable is used as one of the existing
                        # arguments of a target subexpression.
                        subexpr_type_requests.extend([
                            type_request_flat[:i] + type_request_flat[i + 1:]
                            for i, type_i in enumerate(type_request_flat)
                            if type_i == bound_var_type
                        ])

                        # # The subexpression might take this variable as an additional
                        # # argument in any position. We have to enumerate all possibilities
                        # # -- yikes!
                        # for insertion_point in range(len(type_request)):
                        #   subexpr_type_requests.append(type_request[:insertion_point] + (bound_var_type,)
                        #       + type_request[insertion_point:])
                    # print("\t" * (6-max_depth), "λ %s :: %s" % (bound_var, bound_var_type), type_request, subexpr_type_requests)
                    # print("\t" * (6-max_depth), "Now recursing with max_depth=%i" % (max_depth - 1))

                    for subexpr_type_request in subexpr_type_requests:
                        if isinstance(subexpr_type_request, tuple):
                            if not subexpr_type_request:
                                continue
                            else:
                                subexpr_type_request = self.types.make_function_type(
                                    subexpr_type_request)

                        results = self._iter_expressions_inner(
                            max_depth=max_depth - 1,
                            bound_vars=subexpr_bound_vars,
                            type_request=subexpr_type_request,
                            function_weights=function_weights)
                        for expr in results:
                            candidate = l.LambdaExpression(bound_var, expr)
                            valid = self._valid_lambda_expr(
                                candidate, bound_vars)
                            # print("\t" * (6 - max_depth), "valid lambda %s? %s" % (candidate, valid))
                            if self._valid_lambda_expr(candidate, bound_vars):
                                # Assign variable types before returning.
                                extra_types = {
                                    bound_var.name: bound_var.type
                                    for bound_var in subexpr_bound_vars
                                }

                                try:
                                    # TODO make sure variable names are unique before this happens
                                    self.typecheck(candidate, extra_types)
                                except l.InconsistentTypeHierarchyException:
                                    pass
                                else:
                                    yield candidate
            elif expr_type == l.IndividualVariableExpression:
                for bound_var in bound_vars:
                    if type_request and not bound_var.type.matches(
                            type_request):
                        continue

                    # print("\t" * (6-max_depth), "var %s" % bound_var)

                    yield l.IndividualVariableExpression(bound_var)
            elif expr_type == l.ConstantExpression:
                for constant in self.constants:
                    if type_request is not None and not constant.type.matches(
                            type_request):
                        continue

                    yield l.ConstantExpression(constant)
            elif expr_type == l.FunctionVariableExpression:
                # NB we don't support enumerating bound variables with function types
                # right now -- the following only considers yielding fixed functions
                # from the ontology.
                for function in self.functions:
                    # Be a little strict here to avoid excessive enumeration -- only
                    # consider emitting functions when the type request specifically
                    # demands a function, not e.g. AnyType
                    if type_request is None or type_request == self.types.ANY_TYPE \
                        or not function.type.matches(type_request):
                        continue

                    yield l.FunctionVariableExpression(
                        l.Variable(function.name, function.type))
示例#6
0
def read_ec_sexpr(sexpr):
    """
  Parse an EC-style S-expression into an untyped NLTK representation.
  """
    tokens = re.split(r"([()\s])", sexpr)

    bound_vars = set()
    bound_var_stack = []

    is_call = False
    stack = [(None, None, [])]
    for token in tokens:
        token = token.strip()
        if not token:
            continue

        if token == "(":
            if is_call:
                # Second consecutive left-paren -- this means we have a complex
                # function expression.
                stack.append((l.ApplicationExpression, None, []))
            is_call = True
        elif token == "lambda":
            is_call = False
            variable = next_bound_var(bound_vars, l.ANY_TYPE)
            bound_vars.add(variable)
            bound_var_stack.append(variable)

            stack.append((l.LambdaExpression, None, []))
        elif is_call:
            head = token
            if head.startswith("$"):
                bruijn_index = int(head[1:])
                # Bound variable is the head of an application expression.
                # First replace with a function-looking variable, then update parser
                # state.
                var_idx = -1 - bruijn_index
                var = bound_var_stack[var_idx]
                bound_vars.remove(var)

                new_var = l.Variable(var.name.upper())
                bound_var_stack[var_idx] = new_var
                bound_vars.add(new_var)

                head = l.FunctionVariableExpression(new_var)

            stack.append((l.ApplicationExpression, head, []))
            is_call = False
        elif token == ")":
            stack_top = stack.pop()
            if stack_top[0] == l.ApplicationExpression:
                _, pred, args = stack_top
                result = make_application(pred, args)
            elif stack_top[0] == l.LambdaExpression:
                _, _, term = stack_top
                variable = bound_var_stack.pop()
                result = l.LambdaExpression(variable, term[0])
            else:
                raise RuntimeError("unknown element on stack", stack_top)

            stack_parent = stack[-1]
            if stack_parent[
                    0] == l.ApplicationExpression and stack_parent[1] is None:
                # We have just finished reading the head of an application expression.
                expr, _, args = stack_parent
                stack[-1] = (expr, result, args)
            else:
                # Add to children of parent node.
                stack_parent[2].append(result)
        elif token.startswith("$"):
            bruijn_index = int(token[1:])
            stack[-1][2].append(
                l.IndividualVariableExpression(bound_var_stack[-1 -
                                                               bruijn_index]))
        else:
            stack[-1][2].append(l.ConstantExpression(l.Variable(token)))

    assert len(stack) == 1
    assert len(stack[0][2]) == 1
    return stack[0][2][0], bound_vars