def resolve_goal_with_rule(self, goal, rule): """ This is a specialization of :func:`etb.datalog.inference.Inference.resolve_goal` where we have a fixed rule that we examine (does the head of that rule match the goal, if so, we add a pending rule). This is used in :func:`etb.datalog.inference.Inference.add_rule`. :parameters: - `goal`: a goal is an internal representation of a literal, i.e., a list of integers. - `rule`: a rule is an internal representation of a rule, i.e., a list of literals (each literal is a list of integers). :returntype: `True` if the function managed to match the `goal` with the head of the `rule`; `False` otherwise """ self.notify() result = False off = model.offset(rule) disjoint_candidate = model.shift_literal(goal, off) subst = model.get_unification_l(disjoint_candidate, rule[0]) if model.is_substitution(subst): result = True new_clause = model.apply_substitution_c(subst, rule, self.term_factory) self.add_pending_rule(new_clause, model.create_resolution_top_down_explanation(rule, goal), goal) self.log.debug('inference.resolve_goal_with_rule: setting {0}: RESOLVED' .format(goal)) self.set_goal_to_resolved(goal) return result
def resolve_goal(self, goal): """ When adding a `goal`, we do 2 things: - we check the Datalog rules (the KB rules -- *not* the pending rules but the rules and facts that make up the actual KB), and introduce any pending rules if we find rules for which the head matches the goal. Note that :func:`etb.datalog.inference.Inference.add_pending_rule` will introduce a new goal consisting of its first body literal (thus obtaining a top-down push of goals). - we check whether any existing claims match this goal via :func:`etb.datalog.inference.Inference.resolve_goal_with_existing_claims`. :parameters: - `goal`: a goal is an internal representation of a literal, i.e., a list of integers. :returntype: `True` if the function managed to resolve the goal with any pending rule or any claim; `False` otherwise """ self.log.debug('inference.resolve_goal: goal {0}'.format(self.term_factory.close_literal(goal))) self.notify() result = False # Try to resolve with Rules candidate_rules = index.get_candidate_matchings(self.logical_state.db_get_rules_index(), goal) for candidate in candidate_rules: # self.log.debug('inference.resolve_goal: candidate {0}' # .format([str(c) for c in self.term_factory.close_literals(candidate)])) off = model.offset(candidate) disjoint_goal = model.shift_literal(goal, off) subst = model.get_unification_l(candidate[0], disjoint_goal) self.log.debug('inference.resolve_goal: subst {0}' .format(dict([(self.term_factory.get_symbol(v), str(self.term_factory.get_symbol(a))) for v, a in subst.items()]))) if model.is_substitution(subst): result = True pending_rule = model.apply_substitution_c(subst, candidate, self.term_factory) self.log.info('unify goal {1}\n with rule {2}\n yields pending rule {0}' .format([str(c) for c in self.term_factory.close_literals(pending_rule)], self.term_factory.close_literal(goal), [str(c) for c in self.term_factory.close_literals(candidate)])) explanation = model.create_resolution_top_down_explanation(candidate, goal) self.add_pending_rule(pending_rule, explanation, goal) # Try to resolve with Claims resolved_with_claims = True #self.resolve_goal_with_existing_claims(goal) if resolved_with_claims: result = True # when we try to resolve we set it as RESOLVED # self.log.debug('inference.resolve_goal: setting {0}: RESOLVED' # .format(self.term_factory.close_literal(goal))) self.set_goal_to_resolved(goal) return result
def propagate_claim_to_pending_clause(self, claim, claim_expl, candidate): """ Propagation step from a claim for a subgoal for a pending rule (candidate) to the generate a new pending rule. """ clause = claim.clause if isinstance(claim, graph.PendingRule) else claim self.log.debug('inference.propagate_claim_to_pending_clause: claim = {0}, candidate = {1}' .format(self.term_factory.close_literals(clause), self.term_factory.close_literals(candidate.clause))) subst = model.get_unification_l(clause[0],candidate.clause[1]) self.log.debug('inference.resolve_claim: candidate {0!s}: {1}' .format([str(c) for c in self.term_factory.close_literals(candidate.clause)], subst)) if model.is_substitution(subst): new_clause = model.remove_first_body_literal(candidate.clause, subst, self.term_factory) cand_expl = self.logical_state.db_get_explanation(candidate) assert cand_expl[0] != "None" #claim_expl = self.logical_state.db_get_explanation(claim) assert claim_expl[0] != "None" explanation = model.create_resolution_bottom_up_explanation(candidate, claim, claim_expl) # NSH: db_add_claim_to_goal has already been done above # update the subgoal (candidate[1]) with the found claim # self.logical_state.db_add_claim_to_goal(candidate[1], claim) self.increase_subgoalindex(candidate) candidate_annotation = self.logical_state.db_get_annotation(candidate) parent_goal = candidate_annotation.goal self.add_pending_rule(new_clause, explanation, parent_goal)
def is_entailed(self, goal): """ Check whether goal is entailed or not. .. warning:: Used only for unit tests """ candidate_claims = index.get_candidate_specializations(self.logical_state.db_claims, goal) for claim in candidate_claims: if model.is_substitution( model.get_unification_l(claim.clause[0], goal)): return True return False
def resolve_pending_rule(self, rule): """ This is symmetrical to a part of what :func:`etb.datalog.inference.Inference.resolve_claim` does: when adding a pending rule, we try to resolve any existing claims with the first body literal of that pending rule, and you add a new pending rule (or claim if the resulting pending rule became a fact). :parameters: - `rule`: an internal representation of a rule, i.e., a list of literals (which are lists of integers). The first element of the list is the head of the rule. :returntype: `None` """ self.log.debug('inference.resolve_pending_rule {0}' .format([str(c) for c in self.term_factory.close_literals(rule)])) self.notify() candidate_claims = index.get_candidate_specializations(self.logical_state.db_get_claims_index(), rule[1]) for candidate in candidate_claims: subst = model.get_unification_l(candidate[0], rule[1]) # self.log.debug('inference.resolve_pending_rule candidate {0}' # .format([str(c) for c in self.term_factory.close_literals(candidate)])) # self.log.debug('inference.resolve_pending_rule subst {0}' # .format(dict([(self.term_factory.get_symbol(v), # str(self.term_factory.get_symbol(a))) for v, a in subst.items()]))) if model.is_substitution(subst): new_clause = model.remove_first_body_literal(rule,subst, self.term_factory) rule_expl = self.logical_state.db_get_explanation(rule) assert rule_expl[0] != "None" cand_expl = self.logical_state.db_get_explanation(candidate) assert cand_expl[0] != "None" explanation = model.create_resolution_bottom_up_explanation(rule, candidate) # self.log.debug('inference.resolve_pending_rule new_clause {0}: {1}' # .format(new_clause, explanation)) self.logical_state.db_add_claim_to_goal(rule[1], candidate, explanation) if model.is_fact(new_clause): self.add_claim(new_clause, explanation) # increase the propogation index also for claims (not only # for pending rules) self.increase_subgoalindex(rule) else: self.add_pending_rule(new_clause, explanation)
def resolve_goal_with_existing_claims(self, goal): """ Try to resolve a `goal` with any existing claims. In other words, verify whether the `goal` can be solved by any existing claims`. :parameters: - `goal`: a goal is an internal representation of a literal, i.e., a list of integers. :returntype: - `True` if at least one claim was found to be a solution for the `goal`; `False` otherwise. """ self.notify() result = False candidate_claims = index.get_candidate_matchings(self.logical_state.db_get_claims_index(), goal) for candidate in candidate_claims: subst = model.get_unification_l(candidate.clause[0], goal) if model.is_substitution(subst): self.logical_state.db_add_claim_to_goal(goal, candidate) result = True return result