def _top_down_eval( self, context, # type: topdown.TopDownTheory.TopDownContext caller # type: topdown.TopDownTheory.TopDownCaller ): # type: (...) -> bool """Evaluation entry point for the non recursive engine We must compute unifiers and clear off as soon as we can giving back control to the theory context. Returns true if we only need one binding and it has been found, false otherwise. """ raw_lit = context.literals[context.literal_index] query_lit = raw_lit.plug(context.binding) answers, bvars, translators = self.z3context.eval(self, query_lit) if isinstance(answers, bool): if answers: return (context.theory._top_down_finish(context, caller) and not caller.find_all) return False for answer in answers: changes = [] for (val, var, trans) in six.moves.zip(answer, bvars, translators): chg = context.binding.add(var, trans.to_os(val), None) changes.append(chg) context.theory._top_down_finish(context, caller) unify.undo_all(changes) if not caller.find_all: return True return False
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 lit.is_builtin()): 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 check_unify(self, atom_string1, atom_string2, msg, change_num, unifier1=None, unifier2=None, recursive_str=False): self.open(msg) (p1, unifier1, p2, unifier2, changes) = self.create_unify( atom_string1, atom_string2, msg, change_num, unifier1=unifier1, unifier2=unifier2, recursive_str=recursive_str) unify.undo_all(changes) self.assertTrue(p1.plug(unifier1) == p1) self.assertTrue(p2.plug(unifier2) == p2) self.close(msg)
def check_unify(self, atom_string1, atom_string2, msg, change_num, unifier1=None, unifier2=None, recursive_str=False): self.open(msg) (p1, unifier1, p2, unifier2, changes) = self.create_unify( atom_string1, atom_string2, msg, change_num, unifier1=unifier1, unifier2=unifier2, recursive_str=recursive_str) unify.undo_all(changes) self.assertEqual(p1, p1.plug(unifier1)) self.assertEqual(p2, p2.plug(unifier2)) self.close(msg)
def _top_down_th(self, context, caller): """Top-down evaluation for the rules in self.""" # LOG.debug("%s._top_down_th(%s)", self.name, context) lit = context.literals[context.literal_index] self._print_call(lit, context.binding, context.depth) for rule in self.head_index(lit.table.table, lit.plug(context.binding)): unifier = self.new_bi_unifier() self._print_note(lit, context.binding, context.depth, "Trying %s" % rule) # Prefer to bind vars in rule head undo = self.bi_unify(self.head(rule), unifier, lit, context.binding, self.name) if undo is None: # no unifier continue if len(self.body(rule)) == 0: if self._top_down_finish(context, caller): unify.undo_all(undo) if not caller.find_all: return True else: unify.undo_all(undo) else: new_context = self.TopDownContext( rule.body, 0, unifier, context, self, context.depth + 1) if self._top_down_eval(new_context, caller): unify.undo_all(undo) if not caller.find_all: return True else: unify.undo_all(undo) self._print_fail(lit, context.binding, context.depth) return False
def _top_down_th(self, context, caller): """Top-down evaluation for the rules in self.""" # LOG.debug("%s._top_down_th(%s)", self.name, context) lit = context.literals[context.literal_index] self._print_call(lit, context.binding, context.depth) for rule in self.head_index(lit.table, lit.plug(context.binding)): # LOG.debug("%s._top_down_th rule: %s", self.name, rule) unifier = self.new_bi_unifier() self._print_note(lit, context.binding, context.depth, "Trying %s" % rule) # Prefer to bind vars in rule head undo = self.bi_unify(self.head(rule), unifier, lit, context.binding, self.name) if undo is None: # no unifier continue if len(self.body(rule)) == 0: if self._top_down_finish(context, caller): unify.undo_all(undo) if not caller.find_all: return True else: unify.undo_all(undo) else: new_context = self.TopDownContext(rule.body, 0, unifier, context, self, context.depth + 1) if self._top_down_eval(new_context, caller): unify.undo_all(undo) if not caller.find_all: return True else: unify.undo_all(undo) self._print_fail(lit, context.binding, context.depth) return False
def match(self, atom, unifier): # LOG.debug("DBTuple matching %s against atom %s in %s", # self, iterstr(atom.arguments), unifier) if len(self.tuple) != len(atom.arguments): return None changes = [] for i in range(0, len(atom.arguments)): val, binding = unifier.apply_full(atom.arguments[i]) # LOG.debug("val(%s)=%s at %s; comparing to object %s", # atom.arguments[i], val, binding, self.tuple[i]) if val.is_variable(): changes.append(binding.add( val, compile.Term.create_from_python(self.tuple[i]), None)) else: if val.name != self.tuple[i]: unify.undo_all(changes) return None return changes
def match(self, atom, unifier): # LOG.debug("DBTuple matching %s against atom %s in %s", # self, iterstr(atom.arguments), unifier) if len(self.tuple) != len(atom.arguments): return None changes = [] for i in range(0, len(atom.arguments)): val, binding = unifier.apply_full(atom.arguments[i]) # LOG.debug("val(%s)=%s at %s; comparing to object %s", # atom.arguments[i], val, binding, self.tuple[i]) if val.is_variable(): changes.append( binding.add( val, compile.Term.create_from_python(self.tuple[i]), None)) else: if val.name != self.tuple[i]: unify.undo_all(changes) return None return changes
def _top_down_builtin(self, context, caller): """Evaluate a table with a builtin semantics. Returns True if done searching and False otherwise. """ lit = context.literals[context.literal_index] self._print_call(lit, context.binding, context.depth) builtin = congressbuiltin.builtin_registry.builtin(lit.table) # copy arguments into variables # PLUGGED is an instance of compile.Literal plugged = lit.plug(context.binding) # PLUGGED.arguments is a list of compile.Term # create args for function args = [] for i in range(0, builtin.num_inputs): # save builtins with unbound vars during evaluation if not plugged.arguments[i].is_object() and caller.save: # 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 assert plugged.arguments[i].is_object(), ( ("Builtins must be evaluated only after their " "inputs are ground: {} with num-inputs {}".format( str(plugged), builtin.num_inputs))) args.append(plugged.arguments[i].name) # evaluate builtin: must return number, string, or iterable # of numbers/strings try: result = builtin.code(*args) except Exception as e: errmsg = "Error in builtin: " + str(e) self._print_note(lit, context.binding, context.depth, errmsg) self._print_fail(lit, context.binding, context.depth) return False # self._print_note(lit, context.binding, context.depth, # "Result: " + str(result)) success = None undo = [] if builtin.num_outputs > 0: # with return values, local success means we can bind # the results to the return value arguments if (isinstance(result, (six.integer_types, float, six.string_types))): result = [result] # Turn result into normal objects result = [compile.Term.create_from_python(x) for x in result] # adjust binding list unifier = self.new_bi_unifier() undo = unify.bi_unify_lists(result, unifier, lit.arguments[builtin.num_inputs:], context.binding) success = undo is not None else: # without return values, local success means # result was True according to Python success = bool(result) if not success: self._print_fail(lit, context.binding, context.depth) unify.undo_all(undo) return False # otherwise, try to finish proof. If success, return True if self._top_down_finish(context, caller, redo=False): unify.undo_all(undo) return True # if fail, return False. else: unify.undo_all(undo) self._print_fail(lit, context.binding, context.depth) return False
def _top_down_builtin(self, context, caller): """Evaluate a table with a builtin semantics. Returns True if done searching and False otherwise. """ lit = context.literals[context.literal_index] self._print_call(lit, context.binding, context.depth) builtin = builtin_registry.builtin(lit.table) # copy arguments into variables # PLUGGED is an instance of compile.Literal plugged = lit.plug(context.binding) # print "plugged: " + str(plugged) # PLUGGED.arguments is a list of compile.Term # create args for function args = [] for i in xrange(0, builtin.num_inputs): assert plugged.arguments[i].is_object(), (( "Builtins must be evaluated only after their " "inputs are ground: {} with num-inputs {}".format( str(plugged), builtin.num_inputs))) args.append(plugged.arguments[i].name) # evaluate builtin: must return number, string, or iterable # of numbers/strings try: result = builtin.code(*args) except Exception as e: errmsg = "Error in builtin: " + str(e) self._print_note(lit, context.binding, context.depth, errmsg) self._print_fail(lit, context.binding, context.depth) return False # self._print_note(lit, context.binding, context.depth, # "Result: " + str(result)) success = None undo = [] if builtin.num_outputs > 0: # with return values, local success means we can bind # the results to the return value arguments if isinstance(result, (int, long, float, basestring)): result = [result] # Turn result into normal objects result = [compile.Term.create_from_python(x) for x in result] # adjust binding list unifier = self.new_bi_unifier() undo = unify.bi_unify_lists(result, unifier, lit.arguments[builtin.num_inputs:], context.binding) # print "unifier: " + str(undo) success = undo is not None else: # without return values, local success means # result was True according to Python success = bool(result) # print "success: " + str(success) if not success: self._print_fail(lit, context.binding, context.depth) unify.undo_all(undo) return False # otherwise, try to finish proof. If success, return True if self._top_down_finish(context, caller, redo=False): unify.undo_all(undo) return True # if fail, return False. else: unify.undo_all(undo) self._print_fail(lit, context.binding, context.depth) return False