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)
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
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
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
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)
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)