def successors(self, node): """ Successor nodes are possible next pattern elements that can be unified. """ sub = dict(node.state) terms, f_terms, index = node.extra # Figure out best term to match (only need to choose 1 and don't need # to backtrack over choice). for term in [t for necessary in terms if meets_requirements(necessary, sub) for t in terms[necessary] if not is_negated_term(t)]: # Pretty sure this is ok AND faster. key = index_key(subst(sub, term)) # key = index_key(term) if key not in index: return facts = [f for f in index[key]] # shuffle(facts) for fact in facts: new_sub = unify(term, fact, sub) if new_sub is None: continue new_terms = update_terms(terms, f_terms, new_sub, index, partial=True) if new_terms is None: continue yield Node(frozenset(new_sub.items()), node, None, 0, (new_terms, f_terms, index))
def update_neg_pattern(neg_pattern, sub, index, free_vars=None): if free_vars is None: free_vars = set() new_neg_pattern = [] bound_set = set(sub).union(free_vars) for term in neg_pattern: args = set(e for e in extract_strings(term) if is_variable(e)) if args.issubset(bound_set): bterm = execute_functions(subst(sub, term)) if bterm is False: continue if bterm is True: return None key = index_key(bterm) if key in index and len(index[key]) > 0: for fact in index[key]: if unify(bterm, fact, sub, index): return None else: new_neg_pattern.append(term) return new_neg_pattern
def test_term_unification(): # Base Terms assert unify('A', 'B') is None assert unify('A', 'A') == {} # Relations assert unify(('on', 'A'), ('on', 'B')) is None assert unify(('on', 'A'), ('on', 'A')) == {} assert unify(('on', 'A'), ('on', '?x')) == {'?x': 'A'} assert unify(('on', 'A'), ('?rel', 'A')) == {'?rel': 'on'} assert unify(('on', 'A'), ('?rel', '?x')) == {'?rel': 'on', '?x': 'A'} assert unify(('on', '?x'), ('?rel', 'B')) == {'?rel': 'on', '?x': 'B'} assert unify(('on', '?x'), ('?rel', 'B')) == {'?rel': 'on', '?x': 'B'}
def successors(self, node): """ Successor nodes are possible next pattern elements that can be unified. """ sub = dict(node.state) terms, f_terms, index = node.extra # Figure out best term to match (only need to choose 1 and don't need # to backtrack over choice). p_terms = [(len(index[subst(sub, t)]) if t in index else 0, len(necessary), random(), t) for necessary in terms if meets_requirements(necessary, sub) for t in terms[necessary] if not is_negated_term(t)] if len(p_terms) == 0: return p_terms.sort() term = p_terms[0][3] # TODO need to figure out how to handle positiver terms with functional # Pretty sure this is ok AND faster. key = index_key(subst(sub, term)) # key = index_key(term) if key not in index: return facts = [f for f in index[key]] # could do something here where I pick the fact that yields # substitutions that are the LEAST constraining. # I'm not sure if it is worth the time though. shuffle(facts) for fact in facts: # TODO what do we do when the term contains a functional? new_sub = unify(term, fact, sub) if new_sub is None: continue new_terms = update_terms(terms, f_terms, new_sub, index) if new_terms is None: continue yield Node(frozenset(new_sub.items()), node, None, 0, (new_terms, f_terms, index))
def update_terms(terms, f_terms, sub, index, partial=False): new_terms = {} for necessary in terms: # if necessary.issubset(sub): if meets_requirements(necessary, sub): for term in terms[necessary]: bterm = subst(sub, term) if term in f_terms: bterm = execute_functions(bterm) if is_negated_term(bterm): if bterm is False: continue if bterm is True: return None else: if bterm is False: return None if bterm is True: continue if is_negated_term(bterm): key = index_key(bterm[1]) if key in index and len(index[key]) > 0: for fact in index[key]: if unify(bterm[1], fact, sub, index): # if partial: # continue return None else: key = index_key(bterm) if key not in index: if partial: continue return None if bterm not in index[key]: if necessary not in new_terms: new_terms[necessary] = [] new_terms[necessary].append(term) else: new_terms[necessary] = terms[necessary] return new_terms