def get_prediction_goal(self) -> Term: prediction_goal_term = Term(self.functor) nb_of_args = len(self.modes) arguments = [Var('#')] * nb_of_args prediction_goal_term = prediction_goal_term(*arguments) prediction_goal_term = prediction_goal_term.apply(TypeModeLanguage.ReplaceNew(0)) return prediction_goal_term
def apply_substitution_to_term(term: Term, substitution: Dict[str, Term]) -> Term: complete_substitution = {} # NOTE: all variables in the term need to be defined in the substitution for var in term.variables(): complete_substitution[var.name] = Var(var.name) complete_substitution.update(substitution) term_substitution = term.apply(complete_substitution) return term_substitution
def refine(self, rule: Rule): """ORIGINAL: generate ONE refinement for the given rule. We refine a rule by adding LITERALS to the BODY of the rule. :param rule: rule for which to generate refinements :return: GENERATOR of literals that can be added to the rule :rtype: collections.Iterable[Term] """ # 1. we need to know how many variables are in the already existing rule. # We also need to know the types of these variables. # This is important information when adding new literals. varcount = len(rule.get_variables()) variables = self.get_variable_types(*rule.get_literals()) # We check the most recently added body literal. # If there is a most recently added body literal, # then we check whether its functor is '_recursive' # If its most recently added body literal is '_recursive', # we cannot extend the body any further, # so the function returns. # Else, we get the set of already generated literals from the last added literal # If there is no recently added body literal, # we will have to start from scratch, from an empty set if rule.get_literal(): if rule.get_literal().functor == '_recursive': return # can't extend after recursive generated = rule.get_literal().refine_state else: generated = set() # We need to generate a literal to refine the body of the clause # There are three possible ways we can add a literal # 1. as a positive refinement # 2. as a negative refinement # 3. as a recursive refinement # 1) a positive refinement # We have defined which literals can be added in MODES, # as their functor and the mode of each of the arguments. # We will consider each functor as a possible candidate for refinement # # SO for each functor in MODES for functor, modestr in self._refinement_modes: # We have defined which literals can be added in MODES, # as their functor and the mode of each of the arguments. # We will consider each functor as a possible candidate for refinement # we will collect the possible arguments for the current functor in a list arguments = [] arity = len(modestr) # get the types of the arguments of the given predicate types = self.get_argument_types(functor, arity) # a functor has multiple variables, # each with their own modes. # We have to consider each variable with its mode separately # There are three different modes for an argument: # '+': the variable must be unified with an existing variable # --> use an existing variable # '-': create a new variable, do not reuse an old one # 'c': insert a constant # for each argument of functor: # check its mode # '+' --> add to arguments # a list of the variables in the rule with the same type as the current argument # e.g. arguments = [ ..., [X,Y], ...] # '-' --> add to arguments # a list containing 1 new Var # # 'c' --> add to arguments # a list of all possible constants for the type of the argument for argmode, argtype in zip(modestr, types): if argmode == '+': # All possible variables of the given type arguments.append(variables.get(argtype, [])) elif argmode == '-': # A new variable arguments.append([Var('#')]) # what about adding a term a(X,X) where X is new? elif argmode == 'c': # Add a constant arguments.append(self.get_type_values(argtype)) pass else: raise ValueError("Unknown mode specifier '%s'" % argmode) # arguments is a list of lists. # It contains as many lists as the arity of the functor. # Each list corresponds to the possible values in the refined literal of the corresponding variable. # To generate all possible combinations, # we take the carthesian product of the elements in the lists. # # SO for each possible combination of arguments: # create a term using the functor and the arguments. # IF the term hasn't already been generated in the past: # IF we want to break symmetries # add the term to the set of already generated terms # Substitute each of the '#'-vars for a new variable # add the term t as the prototype of the substuted term # We know want to be return the substituted term # But before returning, # we want to save the state of this function, # so we can generate new terms next time we call this function. # To do this, we add the set of generated terms to the new literal. # IF we want to break symmetry, # save a shallow copy of the set of generated literals # ELSE # save the UNION of the seg of generated literals and {t, -t, t_i, -t_i} for args in product(*arguments): t = Term(functor, *args) if t not in generated: if self._symmetry_breaking: generated.add(t) t_i = t.apply(TypeModeLanguage.ReplaceNew(varcount)) t_i.prototype = t if self._symmetry_breaking: t_i.refine_state = generated.copy() else: t_i.refine_state = generated | {t, -t, t_i, -t_i} yield t_i # 2) a negative refinement if self._allow_negation: for functor, modestr in self._refinement_modes: if '-' in modestr: # No new variables allowed for negative literals continue arguments = [] arity = len(modestr) types = self.get_argument_types(functor, arity) for argmode, argtype in zip(modestr, types): if argmode == '+': # All possible variables of the given type arguments.append(variables.get(argtype, [])) elif argmode == 'c': # Add a constant arguments.append(self.get_type_values(argtype)) pass else: raise ValueError("Unknown mode specifier '%s'" % argmode) for args in product(*arguments): t = -Term(functor, *args) if t not in generated: if self._symmetry_breaking: generated.add(t) t_i = t.apply(TypeModeLanguage.ReplaceNew(varcount)) t_i.prototype = t if self._symmetry_breaking: t_i.refine_state = generated.copy() else: t_i.refine_state = generated | {t, -t, t_i, -t_i} yield t_i # 3) recursive if self._allow_recursion and rule.previous.previous: # recursion in the first rule makes no sense types = self.get_argument_types(rule.target.functor, rule.target.arity) arguments = [] for argtype in types: arguments.append(variables.get(argtype, [])) for args in product(*arguments): t = Term('_recursive', *args) t.prototype = t if self._symmetry_breaking: generated.add(t) if self._symmetry_breaking: t.refine_state = generated.copy() else: t.refine_state = generated | {t} yield t
def refine(self, rule): """Generate one refinement for the given rule. :param rule: rule for which to generate refinements :return: generator of literals that can be added to the rule :rtype: collections.Iterable[Term] """ varcount = len(rule.get_variables()) variables = self.get_variable_types(*rule.get_literals()) if rule.get_literal(): if rule.get_literal().functor == '_recursive': return # can't extend after recursive generated = rule.get_literal().refine_state else: generated = set() # 1) a positive refinement for functor, modestr in self._modes: arguments = [] arity = len(modestr) types = self.get_argument_types(functor, arity) for argmode, argtype in zip(modestr, types): if argmode == '+': # All possible variables of the given type arguments.append(variables.get(argtype, [])) elif argmode == '-': # A new variable arguments.append([Var('#')]) # what about adding a term a(X,X) where X is new? elif argmode == 'c': # Add a constant arguments.append(self.get_type_values(argtype)) pass else: raise ValueError("Unknown mode specifier '%s'" % argmode) for args in product(*arguments): t = Term(functor, *args) if t not in generated: if self._symmetry_breaking: generated.add(t) t_i = t.apply(TypeModeLanguage.ReplaceNew(varcount)) t_i.prototype = t if self._symmetry_breaking: t_i.refine_state = generated.copy() else: t_i.refine_state = generated | {t, -t, t_i, -t_i} yield t_i # 2) a negative refinement if self._allow_negation: for functor, modestr in self._modes: if '-' in modestr: # No new variables allowed for negative literals continue arguments = [] arity = len(modestr) types = self.get_argument_types(functor, arity) for argmode, argtype in zip(modestr, types): if argmode == '+': # All possible variables of the given type arguments.append(variables.get(argtype, [])) elif argmode == 'c': # Add a constant arguments.append(self.get_type_values(argtype)) pass else: raise ValueError("Unknown mode specifier '%s'" % argmode) for args in product(*arguments): t = -Term(functor, *args) if t not in generated: if self._symmetry_breaking: generated.add(t) t_i = t.apply(TypeModeLanguage.ReplaceNew(varcount)) t_i.prototype = t if self._symmetry_breaking: t_i.refine_state = generated.copy() else: t_i.refine_state = generated | {t, -t, t_i, -t_i} yield t_i # 3) recursive if self._allow_recursion and rule.previous.previous: # recursion in the first rule makes no sense types = self.get_argument_types(rule.target.functor, rule.target.arity) arguments = [] for argtype in types: arguments.append(variables.get(argtype, [])) for args in product(*arguments): t = Term('_recursive', *args) t.prototype = t if self._symmetry_breaking: generated.add(t) if self._symmetry_breaking: t.refine_state = generated.copy() else: t.refine_state = generated | {t} yield t
def unify_value_different_contexts(value1: Term, value2: Term, source_values, target_values): """ Unify two values that exist in different contexts. Updates the mapping of variables from value1 to values from value2. :param value1: :param value2: :param source_values: mapping of source variable to target value :param target_values: mapping of target variable to TARGET value """ # Variables are negative numbers or None if is_variable(value1) and is_variable(value2): if value1 is None: # value1 is anonyous var pass elif value2 is None: # value2 is anonymous pass else: # Two named variables # Check whether value2 is already linked to another value in the target context sv2 = target_values.get(value2, value2) if sv2 == value2: # no # Check whether value1 is already linked to a value sv1 = source_values.get(value1) if sv1 is None: # no # We can link variable 1 with variable 2 source_values[value1] = value2 else: # yes: we need to unify sv1 and value2 (they both are in target scope) unify_value(sv1, value2, target_values) else: # value2 is already linked to another value # we need to unify value1 with that value unify_value_different_contexts(value1, sv2, source_values, target_values) # only value1 is a variable; value2 is a nonvar term elif is_variable(value1): if value1 is None: pass else: sv1 = source_values.get(value1) if sv1 is None: source_values[value1] = value2 elif is_variable(sv1): if sv1 in value2.variables(): raise OccursCheck() if sv1 != value2: target_values[sv1] = value2 source_values[value1] = value2 else: # unify in same context target_values source_values[value1] = unify_value(source_values[value1], value2, target_values) # only value2 is a variable; value1 is a nonvar term elif is_variable(value2): sv2 = target_values.get(value2) if sv2 is None: target_values[value2] = value1.apply(source_values) elif is_variable(sv2): pass else: unify_value_different_contexts(value1, sv2, source_values, target_values) # value1 and value2 are both not variables # but they are terms with the same signature elif value1.signature == value2.signature: # Assume Term for a1, a2 in zip(value1.args, value2.args): unify_value_different_contexts(a1, a2, source_values, target_values) # they don't have the same signature else: raise UnifyError()