Beispiel #1
0
    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
Beispiel #2
0
    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