Esempio n. 1
0
 def test_lisp_to_nested_expression(self):
     logical_form = "((reverse fb:row.row.year) (fb:row.row.league fb:cell.usl_a_league))"
     expression = util.lisp_to_nested_expression(logical_form)
     assert expression == [['reverse', 'fb:row.row.year'], ['fb:row.row.league', 'fb:cell.usl_a_league']]
     logical_form = "(count (and (division 1) (tier (!= null))))"
     expression = util.lisp_to_nested_expression(logical_form)
     assert expression == ['count', ['and', ['division', '1'], ['tier', ['!=', 'null']]]]
Esempio n. 2
0
 def test_lisp_to_nested_expression(self):
     logical_form = "((reverse fb:row.row.year) (fb:row.row.league fb:cell.usl_a_league))"
     expression = util.lisp_to_nested_expression(logical_form)
     assert expression == [
         ["reverse", "fb:row.row.year"],
         ["fb:row.row.league", "fb:cell.usl_a_league"],
     ]
     logical_form = "(count (and (division 1) (tier (!= null))))"
     expression = util.lisp_to_nested_expression(logical_form)
     assert expression == [
         "count", ["and", ["division", "1"], ["tier", ["!=", "null"]]]
     ]
Esempio n. 3
0
    def parse_logical_form(self,
                           logical_form: str,
                           remove_var_function: bool = True) -> Expression:
        """
        Takes a logical form as a string, maps its tokens using the mapping and returns a parsed expression.

        Parameters
        ----------
        logical_form : ``str``
            Logical form to parse
        remove_var_function : ``bool`` (optional)
            ``var`` is a special function that some languages use within lambda functions to
            indicate the usage of a variable. If your language uses it, and you do not want to
            include it in the parsed expression, set this flag. You may want to do this if you are
            generating an action sequence from this parsed expression, because it is easier to let
            the decoder not produce this function due to the way constrained decoding is currently
            implemented.
        """
        if not logical_form.startswith("("):
            logical_form = f"({logical_form})"
        if remove_var_function:
            # Replace "(x)" with "x"
            logical_form = re.sub(r'\(([x-z])\)', r'\1', logical_form)
            # Replace "(var x)" with "(x)"
            logical_form = re.sub(r'\(var ([x-z])\)', r'(\1)', logical_form)
        parsed_lisp = util.lisp_to_nested_expression(logical_form)
        translated_string = self._process_nested_expression(parsed_lisp)
        type_signature = self.local_type_signatures.copy()
        type_signature.update(self.global_type_signatures)
        return self._logic_parser.parse(translated_string,
                                        signature=type_signature)
Esempio n. 4
0
    def logical_form_to_action_sequence(self, logical_form: str) -> List[str]:
        """
        Converts a logical form into a linearization of the production rules from its abstract
        syntax tree.  The linearization is top-down, depth-first.

        Each production rule is formatted as "LHS -> RHS", where "LHS" is a single non-terminal
        type, and RHS is either a terminal or a list of non-terminals (other possible values for
        RHS in a more general context-free grammar are not produced by our grammar induction
        logic).

        Non-terminals are `types` in the grammar, either basic types (like ``int``, ``str``, or
        some class that you define), or functional types, represented with angle brackets with a
        colon separating arguments from the return type.  Multi-argument functions have commas
        separating their argument types.  For example, ``<int:int>`` is a function that takes an
        integer and returns an integer, and ``<int,int:int>`` is a function that takes two integer
        arguments and returns an integer.

        As an example translation from logical form to complete action sequence, the logical form
        ``(add 2 3)`` would be translated to ``['@start@ -> int', 'int -> [<int,int:int>, int, int]',
        '<int,int:int> -> add', 'int -> 2', 'int -> 3']``.
        """
        expression = util.lisp_to_nested_expression(logical_form)
        try:
            transitions, start_type = self._get_transitions(expression,
                                                            expected_type=None)
            if self._start_types and start_type not in self._start_types:
                raise ParsingError(
                    f"Expression had unallowed start type of {start_type}: {expression}"
                )
        except ParsingError as error:
            logger.error(
                f'Error parsing logical form: {logical_form}: {error}')
            raise
        transitions.insert(0, f'@start@ -> {start_type}')
        return transitions
 def execute(self, logical_form: str):
     """Executes a logical form, using whatever predicates you have defined."""
     if not hasattr(self, "_functions"):
         raise RuntimeError("You must call super().__init__() in your Language constructor")
     logical_form = logical_form.replace(",", " ")
     expression = util.lisp_to_nested_expression(logical_form)
     return self._execute_expression(expression)
Esempio n. 6
0
 def execute(self, lf_raw: str) -> int:
     """
     Very basic model for executing friction logical forms. For now returns answer index (or
     -1 if no answer can be concluded)
     """
     # Remove "a:" prefixes from attributes (hack)
     logical_form = re.sub(r"\(a:", r"(", lf_raw)
     parse = util.lisp_to_nested_expression(logical_form)
     if len(parse) < 2:
         return -1
     if parse[0] == 'infer':
         args = [self._exec_and(arg) for arg in parse[1:]]
         if None in args:
             return -1
         return self._exec_infer(*args)
     return -1
def main():
    def nested_expression_to_lisp(nested_expression):
        if isinstance(nested_expression, str):
            return nested_expression

        elif isinstance(nested_expression, List):
            lisp_expressions = [nested_expression_to_lisp(x) for x in nested_expression]
            return "(" + " ".join(lisp_expressions) + ")"
        else:
            raise NotImplementedError

    drop_language = DROPLanguage()
    DROP_predicates = sorted(list(drop_language._functions.keys()))
    print(DROP_predicates)


    print("Non termincal prods")
    non_terminal_prods = drop_language.get_nonterminal_productions()
    print("\n".join(non_terminal_prods))

    print("\n")
    print("All possible prods")
    all_possible_prods = drop_language.all_possible_productions()
    print("\n".join(all_possible_prods))

    exit()


    # program = "(COMPARATIVE (SELECT GET_QUESTION_SPAN) (PARTIAL_GROUP_count (PARTIAL_PROJECT GET_QUESTION_SPAN)) (CONDITION GET_QUESTION_SPAN))"
    program = "(COMPARATIVE (SELECT GET_QUESTION_SPAN) (PSSA GET_QUESTION_SPAN) (CONDITION GET_QUESTION_SPAN))"
    nested_expression = lisp_to_nested_expression(program)
    action_seq = drop_language.logical_form_to_action_sequence(program)
    print(nested_expression)
    print(action_seq)


    nested_expression = ['COMPARATIVE',
                             ['SELECT', 'nationalities registered in Bilbao'],
                             ['PARTIAL_GROUP_count', ['PARTIAL_PROJECT', 'people of #REF']],
                             ['CONDITION', 'is higher than 10']
                        ]
Esempio n. 8
0
def get_explanation(logical_form: str, world_extractions: JsonDict,
                    answer_index: int, world: QuarelWorld) -> List[JsonDict]:
    """
    Create explanation (as a list of header/content entries) for an answer
    """
    output = []
    nl_world = {}
    if world_extractions["world1"] != "N/A" and world_extractions[
            "world1"] != ["N/A"]:
        nl_world["world1"] = nl_world_string(world_extractions["world1"])
        nl_world["world2"] = nl_world_string(world_extractions["world2"])
        output.append({
            "header":
            "Identified two worlds",
            "content": [
                f"""world1 = {nl_world['world1']}""",
                f"""world2 = {nl_world['world2']}""",
            ],
        })
    else:
        nl_world["world1"] = "world1"
        nl_world["world2"] = "world2"
    parse = util.lisp_to_nested_expression(logical_form)
    if parse[0] != "infer":
        return None
    setup = parse[1]
    output.append({
        "header": "The question is stating",
        "content": nl_arg(setup, nl_world)
    })
    answers = parse[2:]
    output.append({
        "header":
        "The answer options are stating",
        "content": [
            "A: " + " and ".join(nl_arg(answers[0], nl_world)),
            "B: " + " and ".join(nl_arg(answers[1], nl_world)),
        ],
    })
    setup_core = setup
    if setup[0] == "and":
        setup_core = setup[1]
    s_attr = setup_core[0]
    s_dir = world.qr_size[setup_core[1]]
    s_world = nl_world[setup_core[2]]
    a_attr = answers[answer_index][0]
    qr_dir = world._get_qr_coeff(strip_entity_type(s_attr),
                                 strip_entity_type(a_attr))
    a_dir = s_dir * qr_dir
    a_world = nl_world[answers[answer_index][2]]

    content = [
        f"When {nl_attr(s_attr)} is {nl_dir(s_dir)} " +
        f"then {nl_attr(a_attr)} is {nl_dir(a_dir)} (for {s_world})"
    ]
    if a_world != s_world:
        content.append(
            f"""Therefore {nl_attr(a_attr)} is {nl_dir(-a_dir)} for {a_world}"""
        )
    content.append(f"Therefore {chr(65+answer_index)} is the correct answer")

    output.append({"header": "Theory used", "content": content})

    return output