示例#1
0
    def _instances(self, rule, index, binding, results, possibilities):
        """Return all instances of the given RULE without evaluating builtins.

        Assumes self.head_index returns rules with empty bodies.
        """
        if index >= len(rule.body):
            results.add(rule.plug(binding))
            return
        lit = rule.body[index]
        self._print_call(lit, binding, 0)
        # if already ground or a builtin, go to the next literal
        if (lit.is_ground() or
                builtin_registry.is_builtin(lit.table, len(lit.arguments))):
            self._instances(rule, index + 1, binding, results, possibilities)
            return
        # Otherwise, find instances in this theory
        if lit.tablename() in possibilities:
            options = possibilities[lit.tablename()]
        else:
            options = self.head_index(lit.tablename(), lit.plug(binding))
        for data in options:
            self._print_note(lit, binding, 0, "Trying: %s" % repr(data))
            undo = unify.match_atoms(lit, binding, self.head(data))
            if undo is None:  # no unifier
                continue
            self._print_exit(lit, binding, 0)
            # recurse on the rest of the literals in the rule
            self._instances(rule, index + 1, binding, results, possibilities)
            if undo is not None:
                unify.undo_all(undo)
            self._print_redo(lit, binding, 0)
        self._print_fail(lit, binding, 0)
示例#2
0
    def _extract_lp_variable_equality_lit(self, lit, rewrite_theory):
        """Identify datalog variable representing an LP-variable.

        :param lit: an instance of Literal
        :param rewrite_theory: reference to a theory that contains rules
               describing how tables correspond to LP variable inputs and
               outputs.
        Returns None, signifying literal does not include any datalog
        variable that maps to an LP variable, or (datalogvar, lpvar).
        """
        if builtin_registry.is_builtin(lit.table, len(lit.arguments)):
            return
        # LOG.info("_extract_lp_var_eq_lit %s", lit)
        rewrites = rewrite_theory.abduce(lit, ['var', 'output'])
        # LOG.info("lit rewriting: %s", ";".join(str(x) for x in rewrites))
        if not rewrites:
            return
        assert(len(rewrites) == 1)
        varlit = next(lit for lit in rewrites[0].body
                      if lit.table.table == 'var')
        # LOG.info("varlit: %s", varlit)
        lpvar = self._varlit_to_lp_variable(varlit)
        outlit = next(lit for lit in rewrites[0].body
                      if lit.table.table == 'output')
        outvar = outlit.arguments[0].name
        # LOG.info("lpvar: %s; outvar: %s", lpvar, outvar)
        return outvar, lpvar
示例#3
0
    def compute_delta_rules(cls, formulas):
        """Return list of DeltaRules computed from formulas.

        Assuming FORMULAS has no self-joins, return a list of DeltaRules
        derived from those FORMULAS.
        """
        # Should do the following for correctness, but it needs to be
        #    done elsewhere so that we can properly maintain the tables
        #    that are generated.
        # formulas = cls.eliminate_self_joins(formulas)
        delta_rules = []
        for rule in formulas:
            if rule.is_atom():
                continue
            rule = compile.reorder_for_safety(rule)
            for literal in rule.body:
                if builtin_registry.is_builtin(literal.table,
                                               len(literal.arguments)):
                    continue
                newbody = [lit for lit in rule.body if lit is not literal]
                delta_rules.append(
                    DeltaRule(literal, rule.head, newbody, rule))
        return delta_rules
示例#4
0
    def compute_delta_rules(cls, formulas):
        """Return list of DeltaRules computed from formulas.

        Assuming FORMULAS has no self-joins, return a list of DeltaRules
        derived from those FORMULAS.
        """
        # Should do the following for correctness, but it needs to be
        #    done elsewhere so that we can properly maintain the tables
        #    that are generated.
        # formulas = cls.eliminate_self_joins(formulas)
        delta_rules = []
        for rule in formulas:
            if rule.is_atom():
                continue
            rule = compile.reorder_for_safety(rule)
            for literal in rule.body:
                if builtin_registry.is_builtin(literal.table,
                                               len(literal.arguments)):
                    continue
                newbody = [lit for lit in rule.body if lit is not literal]
                delta_rules.append(DeltaRule(literal, rule.head, newbody,
                                             rule))
        return delta_rules
示例#5
0
    def _top_down_eval(self, context, caller):
        """Compute instances.

        Compute all instances of LITERALS (from LITERAL_INDEX and above)
        that are true according to the theory (after applying the
        unifier BINDING to LITERALS).
        Returns True if done searching and False otherwise.
        """
        # no recursive rules, ever; this style of algorithm will not terminate
        lit = context.literals[context.literal_index]
        # LOG.debug("CALL: %s._top_down_eval(%s, %s)",
        #     self.name, context, caller)

        # abduction
        if caller.save is not None and caller.save(lit, context.binding):
            self._print_call(lit, context.binding, context.depth)
            # save lit and binding--binding may not be fully flushed out
            #   when we save (or ever for that matter)
            caller.support.append((lit, context.binding))
            self._print_save(lit, context.binding, context.depth)
            success = self._top_down_finish(context, caller)
            caller.support.pop()  # pop in either case
            if success:
                return True
            else:
                self._print_fail(lit, context.binding, context.depth)
                return False

        # regular processing
        if lit.is_negated():
            # LOG.debug("%s is negated", lit)
            # recurse on the negation of the literal
            plugged = lit.plug(context.binding)
            assert plugged.is_ground(), (
                "Negated literal not ground when evaluated: " +
                str(plugged))
            self._print_call(lit, context.binding, context.depth)
            new_context = self.TopDownContext(
                [lit.complement()], 0, context.binding, None,
                self, context.depth + 1)
            new_caller = self.TopDownCaller(caller.variables, caller.binding,
                                            caller.theory, find_all=False,
                                            save=None)
            # Make sure new_caller has find_all=False, so we stop as soon
            #    as we can.
            # Ensure save=None so that abduction does not save anything.
            #    Saving while performing NAF makes no sense.
            if self._top_down_includes(new_context, new_caller):
                self._print_fail(lit, context.binding, context.depth)
                return False
            else:
                # don't need bindings b/c LIT must be ground
                return self._top_down_finish(context, caller, redo=False)
        elif lit.tablename() == 'true':
            self._print_call(lit, context.binding, context.depth)
            return self._top_down_finish(context, caller, redo=False)
        elif lit.tablename() == 'false':
            self._print_fail(lit, context.binding, context.depth)
            return False
        elif builtin_registry.is_builtin(lit.table, len(lit.arguments)):
            self._top_down_builtin(context, caller)
        elif (self.theories is not None and
              lit.theory is not None and
              lit.theory != self.name and
              not lit.is_update()):  # this isn't a modal
            return self._top_down_module(context, caller)
        else:
            return self._top_down_truth(context, caller)
示例#6
0
    def _top_down_eval(self, context, caller):
        """Compute instances.

        Compute all instances of LITERALS (from LITERAL_INDEX and above)
        that are true according to the theory (after applying the
        unifier BINDING to LITERALS).
        Returns True if done searching and False otherwise.
        """
        # no recursive rules, ever; this style of algorithm will not terminate
        lit = context.literals[context.literal_index]
        # LOG.debug("CALL: %s._top_down_eval(%s, %s)",
        #     self.name, context, caller)

        # abduction
        if caller.save is not None and caller.save(lit, context.binding):
            self._print_call(lit, context.binding, context.depth)
            # save lit and binding--binding may not be fully flushed out
            #   when we save (or ever for that matter)
            caller.support.append((lit, context.binding))
            self._print_save(lit, context.binding, context.depth)
            success = self._top_down_finish(context, caller)
            caller.support.pop()  # pop in either case
            if success:
                return True
            else:
                self._print_fail(lit, context.binding, context.depth)
                return False

        # regular processing
        if lit.is_negated():
            # LOG.debug("%s is negated", lit)
            # recurse on the negation of the literal
            plugged = lit.plug(context.binding)
            assert plugged.is_ground(), (
                "Negated literal not ground when evaluated: " + str(plugged))
            self._print_call(lit, context.binding, context.depth)
            new_context = self.TopDownContext([lit.complement()], 0,
                                              context.binding, None, self,
                                              context.depth + 1)
            new_caller = self.TopDownCaller(caller.variables,
                                            caller.binding,
                                            caller.theory,
                                            find_all=False,
                                            save=None)
            # Make sure new_caller has find_all=False, so we stop as soon
            #    as we can.
            # Ensure save=None so that abduction does not save anything.
            #    Saving while performing NAF makes no sense.
            if self._top_down_includes(new_context, new_caller):
                self._print_fail(lit, context.binding, context.depth)
                return False
            else:
                # don't need bindings b/c LIT must be ground
                return self._top_down_finish(context, caller, redo=False)
        elif lit.tablename() == 'true':
            self._print_call(lit, context.binding, context.depth)
            return self._top_down_finish(context, caller, redo=False)
        elif lit.tablename() == 'false':
            self._print_fail(lit, context.binding, context.depth)
            return False
        elif builtin_registry.is_builtin(lit.table, len(lit.arguments)):
            self._top_down_builtin(context, caller)
        elif (self.theories is not None and lit.theory is not None
              and lit.theory != self.name
              and not lit.is_update()):  # this isn't a modal
            return self._top_down_module(context, caller)
        else:
            return self._top_down_truth(context, caller)