예제 #1
0
 def _delete_actual(self, rule):
     """Delete RULE and return True if there was a change."""
     self.dirty = True
     if compile.is_atom(rule):
         rule = compile.Rule(rule, [], rule.location)
     self.log(rule.head.table.table, "Delete: %s", rule)
     return self.rules.discard_rule(rule.head.table.table, rule)
예제 #2
0
    def _extract_lp_variable_equalities(self, rule, rewrite_theory):
        """Extract values for LP variables and slightly modify rule.

        :param rule: an instance of Rule
        :param rewrite_theory: reference to a theory that contains rules
               describing how tables correspond to LP variable inputs and
               outputs.

        Returns (i) dictionary mapping Datalog variable name (a string) to
        the set of LP variables to which it is equal and (ii) a rewriting
        of the rule that is the same as the original except some
        elements have been removed from the body.
        """
        newbody = []
        varnames = {}
        for lit in rule.body:
            result = self._extract_lp_variable_equality_lit(
                lit, rewrite_theory)
            if result is None:
                newbody.append(lit)
            else:
                datalogvar, lpvar = result
                if datalogvar not in varnames:
                    varnames[datalogvar] = set([lpvar])
                else:
                    varnames[datalogvar].add(lpvar)
        return varnames, compile.Rule(rule.head, newbody)
예제 #3
0
 def _insert_actual(self, rule):
     """Insert RULE and return True if there was a change."""
     self.dirty = True
     if compile.is_atom(rule):
         rule = compile.Rule(rule, [], rule.location)
     self.log(rule.head.table.table, "Insert: %s", repr(rule))
     return self.rules.add_rule(rule.head.table.table, rule)
예제 #4
0
    def eliminate_self_joins(cls, formulas):
        """Remove self joins.

        Return new list of formulas that is equivalent to
        the list of formulas FORMULAS except that there
        are no self-joins.
        """
        def new_table_name(name, arity, index):
            return "___{}_{}_{}".format(name, arity, index)

        def n_variables(n):
            vars = []
            for i in xrange(0, n):
                vars.append("x" + str(i))
            return vars

        # dict from (table name, arity) tuple to
        #      max num of occurrences of self-joins in any rule
        global_self_joins = {}
        # remove self-joins from rules
        results = []
        for rule in formulas:
            if rule.is_atom():
                results.append(rule)
                continue
            LOG.debug("eliminating self joins from %s", rule)
            occurrences = {}  # for just this rule
            for atom in rule.body:
                table = atom.tablename()
                arity = len(atom.arguments)
                tablearity = (table, arity)
                if tablearity not in occurrences:
                    occurrences[tablearity] = 1
                else:
                    # change name of atom
                    atom.table = new_table_name(table, arity,
                                                occurrences[tablearity])
                    # update our counters
                    occurrences[tablearity] += 1
                    if tablearity not in global_self_joins:
                        global_self_joins[tablearity] = 1
                    else:
                        global_self_joins[tablearity] = (max(
                            occurrences[tablearity] - 1,
                            global_self_joins[tablearity]))
            results.append(rule)
            LOG.debug("final rule: %s", rule)
        # add definitions for new tables
        for tablearity in global_self_joins:
            table = tablearity[0]
            arity = tablearity[1]
            for i in xrange(1, global_self_joins[tablearity] + 1):
                newtable = new_table_name(table, arity, i)
                args = [compile.Variable(var) for var in n_variables(arity)]
                head = compile.Literal(newtable, args)
                body = [compile.Literal(table, args)]
                results.append(compile.Rule(head, body))
                LOG.debug("Adding rule %s", results[-1])
        return results
예제 #5
0
    def test_add_fact(self):
        fact1 = Fact('p', (1, 2, 3))
        equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())

        self.assertTrue(self.ruleset.add_rule('p', fact1))
        self.assertTrue('p' in self.ruleset)
        self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))
        self.assertEqual(['p'], self.ruleset.keys())
예제 #6
0
    def test_add_equivalent_rule(self):
        # equivalent_rule could be a fact because it has no body, and is
        # ground.
        equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())

        self.assertTrue(self.ruleset.add_rule('p', equivalent_rule))
        self.assertTrue('p' in self.ruleset)
        self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))
        self.assertEqual(['p'], self.ruleset.keys())
예제 #7
0
    def test_discard_equivalent_rule(self):
        fact = Fact('p', (1, 2, 3))
        equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ())

        self.assertTrue(self.ruleset.add_rule('p', fact))
        self.assertTrue('p' in self.ruleset)
        self.assertEqual([equivalent_rule], self.ruleset.get_rules('p'))

        self.assertTrue(self.ruleset.discard_rule('p', equivalent_rule))
        self.assertFalse('p' in self.ruleset)
        self.assertEqual([], self.ruleset.keys())
예제 #8
0
    def get_rules(self, key, match_literal=None):
        facts = []

        if (match_literal and not match_literal.is_negated()
                and key in self.facts):
            # If the caller supplies a literal to match against, then use an
            # index to find the matching rules.
            bound_arguments = tuple([
                i for i, arg in enumerate(match_literal.arguments)
                if not arg.is_variable()
            ])
            if (bound_arguments
                    and not self.facts[key].has_index(bound_arguments)):
                # The index does not exist, so create it.
                self.facts[key].create_index(bound_arguments)

            partial_fact = tuple([
                (i, arg.name) for i, arg in enumerate(match_literal.arguments)
                if not arg.is_variable()
            ])
            facts = list(self.facts[key].find(partial_fact))
        else:
            # There is no usable match_literal, so get all facts for the
            # table.
            facts = list(self.facts.get(key, ()))

        # Convert native tuples to Rule objects.

        # TODO(alex): This is inefficient because it creates Literal and Rule
        # objects.  It would be more efficient to change the TopDownTheory and
        # unifier to handle Facts natively.
        fact_rules = []
        for fact in facts:
            # Setting use_modules=False so we don't split up tablenames.
            #   This allows us to choose at compile-time whether to split
            #   the tablename up.
            literal = compile.Literal(
                key, [compile.Term.create_from_python(x) for x in fact],
                use_modules=False)
            fact_rules.append(compile.Rule(literal, ()))

        return fact_rules + list(self.rules.get(key, ()))
예제 #9
0
    def check(self, rule, data, correct, possibilities=None):
        rule = compile.parse1(rule, use_modules=False)
        data = compile.parse(data, use_modules=False)
        possibilities = possibilities or ''
        possibilities = compile.parse(possibilities, use_modules=False)
        possibilities = [compile.Rule(x, []) for x in possibilities]
        poss = {}
        for rule_lit in possibilities:
            if rule_lit.head.tablename() not in poss:
                poss[rule_lit.head.tablename()] = set([rule_lit])
            else:
                poss[rule_lit.head.tablename()].add(rule_lit)

        th = nonrecursive.MultiModuleNonrecursiveRuleTheory()
        th.debug_mode()
        for lit in data:
            th.insert(lit)
        result = th.instances(rule, poss)
        actual = " ".join(str(x) for x in result)
        e = helper.datalog_equal(actual, correct)
        self.assertTrue(e)
예제 #10
0
    def test_contains(self):
        fact = Fact('p', (1, 2, 3))
        rule = compile.parse1('p(x) :- q(x)')
        self.ruleset.add_rule('p', fact)
        self.ruleset.add_rule('p', rule)

        # positive tests
        equivalent_fact1 = Fact('p', (1, 2, 3))
        equivalent_fact2 = compile.parse1('p(1,2,3)')
        equivalent_fact3 = compile.Rule(compile.parse1('p(1,2,3)'), ())
        equivalent_rule = compile.parse1('p(x) :- q(x)')
        self.assertTrue(self.ruleset.contains('p', equivalent_fact1))
        self.assertTrue(self.ruleset.contains('p', equivalent_fact2))
        self.assertTrue(self.ruleset.contains('p', equivalent_fact3))
        self.assertTrue(self.ruleset.contains('p', equivalent_rule))

        # negative tests
        nonequiv_fact = compile.parse1('p(4, 5, 6)')
        nonequiv_rule = compile.parse1('p(x) :- r(x)')
        self.assertFalse(self.ruleset.contains('p', nonequiv_fact))
        self.assertFalse(self.ruleset.contains('p', nonequiv_rule))
예제 #11
0
    def abduce(self, query, tablenames, find_all=True):
        """Compute additional literals.

        Computes additional literals that if true would make
        (some instance of) QUERY true.  Returns a list of rules
        where the head represents an instance of the QUERY and
        the body is the collection of literals that must be true
        in order to make that instance true.  If QUERY is a rule,
        each result is an instance of the head of that rule, and
        the computed literals if true make the body of that rule
        (and hence the head) true.  If FIND_ALL is true, the
        return list has at most one element.
        Limitation: every negative literal relevant to a proof of
        QUERY is unconditionally true, i.e. no literals are saved
        when proving a negative literal is true.
        """
        assert compile.is_datalog(query), "Explain requires a formula"
        if compile.is_atom(query):
            literals = [query]
            output = query
        else:
            literals = query.body
            output = query.head
        # We need all the variables we will be using in the output, which
        #   here is just the head of QUERY (or QUERY itself if it is an atom)
        abductions = self.top_down_abduction(
            output.variables(),
            literals,
            find_all=find_all,
            save=lambda lit, binding: lit.table in tablenames)
        results = [
            compile.Rule(output.plug(abd.binding), abd.support)
            for abd in abductions
        ]
        self.log(query.tablename(), "abduction result:")
        self.log(query.tablename(), "\n".join([str(x) for x in results]))
        return results
예제 #12
0
    def test_type_checkers(self):
        """Test the type checkers, e.g. is_atom, is_rule."""
        atom = compile.Literal("p", [])
        atom2 = compile.Literal("q", [])
        atom3 = compile.Literal("r", [])
        lit = compile.Literal("r", [], negated=True)
        regular_rule = compile.Rule(atom, [atom2, atom3])
        regular_rule2 = compile.Rule(atom, [lit, atom2])
        multi_rule = compile.Rule([atom, atom2], [atom3])
        fake_rule = compile.Rule([atom, 1], [atom2])
        fake_rule2 = compile.Rule(atom, [atom2, 1])

        # is_atom
        self.assertTrue(compile.is_atom(atom))
        self.assertTrue(compile.is_atom(atom2))
        self.assertTrue(compile.is_atom(atom3))
        self.assertFalse(compile.is_atom(lit))
        self.assertFalse(compile.is_atom(regular_rule))
        self.assertFalse(compile.is_atom(regular_rule2))
        self.assertFalse(compile.is_atom(multi_rule))
        self.assertFalse(compile.is_atom(fake_rule))
        self.assertFalse(compile.is_atom(fake_rule2))
        self.assertFalse(compile.is_atom("a string"))

        # is_literal
        self.assertTrue(compile.is_literal(atom))
        self.assertTrue(compile.is_literal(atom2))
        self.assertTrue(compile.is_literal(atom3))
        self.assertTrue(compile.is_literal(lit))
        self.assertFalse(compile.is_literal(regular_rule))
        self.assertFalse(compile.is_literal(regular_rule2))
        self.assertFalse(compile.is_literal(multi_rule))
        self.assertFalse(compile.is_literal(fake_rule))
        self.assertFalse(compile.is_literal(fake_rule2))
        self.assertFalse(compile.is_literal("a string"))

        # is_regular_rule
        self.assertFalse(compile.is_regular_rule(atom))
        self.assertFalse(compile.is_regular_rule(atom2))
        self.assertFalse(compile.is_regular_rule(atom3))
        self.assertFalse(compile.is_regular_rule(lit))
        self.assertTrue(compile.is_regular_rule(regular_rule))
        self.assertTrue(compile.is_regular_rule(regular_rule2))
        self.assertFalse(compile.is_regular_rule(multi_rule))
        self.assertFalse(compile.is_regular_rule(fake_rule))
        self.assertFalse(compile.is_regular_rule(fake_rule2))
        self.assertFalse(compile.is_regular_rule("a string"))

        # is_multi_rule
        self.assertFalse(compile.is_multi_rule(atom))
        self.assertFalse(compile.is_multi_rule(atom2))
        self.assertFalse(compile.is_multi_rule(atom3))
        self.assertFalse(compile.is_multi_rule(lit))
        self.assertFalse(compile.is_multi_rule(regular_rule))
        self.assertFalse(compile.is_multi_rule(regular_rule2))
        self.assertTrue(compile.is_multi_rule(multi_rule))
        self.assertFalse(compile.is_multi_rule(fake_rule))
        self.assertFalse(compile.is_multi_rule(fake_rule2))
        self.assertFalse(compile.is_multi_rule("a string"))

        # is_rule
        self.assertFalse(compile.is_rule(atom))
        self.assertFalse(compile.is_rule(atom2))
        self.assertFalse(compile.is_rule(atom3))
        self.assertFalse(compile.is_rule(lit))
        self.assertTrue(compile.is_rule(regular_rule))
        self.assertTrue(compile.is_rule(regular_rule2))
        self.assertTrue(compile.is_rule(multi_rule))
        self.assertFalse(compile.is_rule(fake_rule))
        self.assertFalse(compile.is_rule(fake_rule2))
        self.assertFalse(compile.is_rule("a string"))

        # is_datalog
        self.assertTrue(compile.is_datalog(atom))
        self.assertTrue(compile.is_datalog(atom2))
        self.assertTrue(compile.is_datalog(atom3))
        self.assertFalse(compile.is_datalog(lit))
        self.assertTrue(compile.is_datalog(regular_rule))
        self.assertTrue(compile.is_datalog(regular_rule2))
        self.assertFalse(compile.is_datalog(multi_rule))
        self.assertFalse(compile.is_datalog(fake_rule))
        self.assertFalse(compile.is_datalog(fake_rule2))
        self.assertFalse(compile.is_datalog("a string"))

        # is_extended_datalog
        self.assertTrue(compile.is_extended_datalog(atom))
        self.assertTrue(compile.is_extended_datalog(atom2))
        self.assertTrue(compile.is_extended_datalog(atom3))
        self.assertFalse(compile.is_extended_datalog(lit))
        self.assertTrue(compile.is_extended_datalog(regular_rule))
        self.assertTrue(compile.is_extended_datalog(regular_rule2))
        self.assertTrue(compile.is_extended_datalog(multi_rule))
        self.assertFalse(compile.is_extended_datalog(fake_rule))
        self.assertFalse(compile.is_extended_datalog(fake_rule2))
        self.assertFalse(compile.is_extended_datalog("a string"))