Пример #1
0
def unify_value(value1, value2, source_values):
    """
    Unify two values that exist in the same context.
    :param value1:
    :param value2:
    :param source_values:
    :return:
    """
    # Variables are negative numbers or None
    # Naive implementation (no occurs check)

    if is_variable(value1) and is_variable(value2):
        if value1 == value2:
            return value1
        elif value1 is None:
            return value2
        if value2 is None:  # second one is anonymous
            return value1
        else:
            # Two named variables: unify their source_values
            value = unify_value(source_values.get(value1), source_values.get(value2), source_values)
            if value is None:
                value = max(value1, value2)
            if value1 != value:
                source_values[value1] = value
            if value2 != value:
                source_values[value2] = value
            # Return the one of lowest rank: negative numbers, so max
            return value
    elif is_variable(value1):
        if value1 is None:
            return value2
        else:
            if value1 in value2.variables():
                raise OccursCheck()
            value = unify_value(source_values.get(value1), value2, source_values)
            source_values[value1] = value
            return value
    elif is_variable(value2):
        if value2 is None:
            return value1
        else:
            if value2 in value1.variables():
                raise OccursCheck()
            value = unify_value(source_values.get(value2), value1, source_values)
            source_values[value2] = value
            return value
    elif value1.signature == value2.signature:  # Assume Term
        return value1.with_args(*[unify_value(a1, a2, source_values)
                                  for a1, a2 in zip(value1.args, value2.args)])
    else:
        raise UnifyError()
Пример #2
0
    def get_variable_types(self, *literals: Iterable[Term]) -> Dict[TypeName, Set[Term]]:
        """Get the types of all variables that occur in the given literals.

        :param literals: literals to extract variables from
        :type literals: collections.Iterable[Term]
        :return: dictionary with list of variables for each type
        :rtype: dict[str, list[Term]]
        """
        result = defaultdict(set)
        for lit in literals:  # type: Term
            if lit is None:
                pass
            else:
                if lit.is_negated():
                    lit = -lit

                if lit.functor == '_recursive':
                    # Don't need to process this, variables will occur somewhere else
                    #  because _recursive has mode + on all arguments.
                    continue
                argument_types = self.get_argument_types(lit.functor, lit.arity)  # type: TypeArguments
                for arg, argtype in zip(lit.args, argument_types):  # type: Term, TypeName
                    if is_variable(arg) or arg.is_var():
                        result[argtype].add(arg)
        return result
Пример #3
0
    def __getitem__(self, key):
        if key is None:
            return None
        elif key < 0:
            value = self.numbers.get(key)
            if value is None:
                self.num_count -= 1
                value = self.num_count
                self.numbers[key] = value
            self.translate[value] = key
        else:
            value = self.context[key]
            if value is None or type(value) == int:
                if value is not None:
                    key = value
                    okey = value
                else:
                    okey = None
                value = self.numbers.get(key)
                if value is None:
                    self.num_count -= 1
                    value = self.num_count
                    self.numbers[key] = value
                self.translate[value] = okey

        if not is_variable(value):
            value = value.apply(self)
        return value
Пример #4
0
def term2list2(term):
    result = []
    while not is_variable(term) and term.functor == '.' and term.arity == 2:
        result.append(term.args[0])
        term = term.args[1]
    if not term == Term('[]'):
        raise ValueError('Expected fixed list.')
    return result
Пример #5
0
def instantiate(term, context):
    """Replace variables in Term by values based on context lookup table.

    :param term:
    :param context:
    :return:
    """
    if is_variable(term):
        return context[term]
    else:
        return term.apply(context)
Пример #6
0
def _unify_call_head_single(source_value, target_value, target_context, source_values):
    """
    Unify a call argument with a clause head argument.
    :param source_value: value occuring in clause call
    :type source_value: Term or variable. All variables are represented as negative numbers.
    :param target_value: value occuring in clause head
    :type target_value: Term or variable. All variables are represented as positive numbers \
    corresponding to positions
    in target_context.
    :param target_context: values of the variables in the current context. Output argument.
    :param source_values:
    :raise UnifyError: unification failed

    The values in the target_context contain only variables from the source context \
    (i.e. negative numbers) (or Terms).
    Initially values are set to None.
    """
    if is_variable(target_value):  # target_value is variable (integer >= 0)
        assert type(target_value) == int and target_value >= 0
        target_context[target_value] = \
            unify_value(source_value, target_context[target_value], source_values)
    else:  # target_value is a Term (which can still contain variables)
        if is_variable(source_value):  # source value is variable (integer < 0)
            if source_value is None:
                pass
            else:
                assert type(source_value) == int and source_value < 0
                # This means that *all* occurrences of source_value should unify with the same value.
                # We keep track of the value for each source_value, and unify the target_value with the current value.
                # Note that we unify two values in the same scope.
                source_values[source_value] = \
                    unify_value(source_values.get(source_value), target_value, source_values)
        else:  # source value is a Term (which can still contain variables)
            if target_value.signature == source_value.signature:
                # When signatures match, recursively unify the arguments.
                for s_arg, t_arg in zip(source_value.args, target_value.args):
                    _unify_call_head_single(s_arg, t_arg, target_context, source_values)
            else:
                raise UnifyError()
Пример #7
0
def substitute_all(terms, subst, wrapped=False):
    """

    :param terms:
    :param subst:
    :return:
    """
    if not wrapped:
        subst = _SubstitutionWrapper(subst)
    result = []
    for term in terms:
        if is_variable(term):
            result.append(subst[term])
        else:
            result.append(term.apply(subst))
    return result
Пример #8
0
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()
    def load(cls, filedata):
        engine = DefaultEngine()

        cores_l = []
        cores = []
        modes = []
        types = {}
        constants = {}
        constant_modes = []
        
        # Load the cores.
        cores_str = '\n'.join(filedata.get('CORES', []))
        for core in PrologString(cores_str):
            core_literals = Clause.literals_from_logic(core)
            cores_l.append(core_literals)

        # Load the modes.
        modes_str = '\n'.join(filedata.get('MODES', []))
        for mode_term in PrologString(modes_str):
            if mode_term.is_negated():
                mode_term = -mode_term
                modes.append((False, mode_term))
            else:
                modes.append((True, mode_term))
            for i, a in enumerate(mode_term.args):
                if cls.mode_is_constant(a):
                    constant_modes.append((mode_term.signature, i))

        # Load the types.
        all_types = set()
        types_str = '\n'.join(filedata.get('TYPES', []))
        for type_term in PrologString(types_str):
            predicate = type_term.signature
            argtypes = type_term.args
            for argtype in argtypes:
                all_types.add(argtype)
            types[predicate] = argtypes

        # Load the constants.
        constants_str = '\n'.join(filedata.get('CONSTANTS', []))
        constants_db = engine.prepare(PrologString(constants_str))
        for t in all_types:
            values = [x[0] for x in engine.query(constants_db, t(None))]
            if values:
                constants[t] = values

        # Change variables in cores
        for core_l in cores_l:
            core = []
            varreplace = {}
            vars_head = set()
            vars_body = set()
            new_vars = 0
            for lit in core_l:
                if lit.is_negated():
                    negated = True
                    lit = -lit
                else:
                    negated = False
                new_args = []
                for arg_i, arg_v in enumerate(lit.args):
                    if isinstance(arg_v, Var) or is_variable(arg_v):
                        if arg_v in varreplace:
                            new_var = varreplace[arg_v]
                            new_args.append(new_var)
                        else:
                            if lit.signature not in types:
                                raise InvalidLanguage('No type specified for predicate \'%s\'.' % lit.signature)
                            argtype = types[lit.signature][arg_i]
                            new_var = TypedVar(new_vars, argtype)
                            new_vars += 1
                            varreplace[arg_v] = new_var
                            new_args.append(new_var)
                        if negated:
                            vars_body.add(new_var)
                        else:
                            vars_head.add(new_var)
                    else:
                        new_args.append(arg_v)
                new_lit = lit(*new_args)
                if negated:
                    new_lit = -new_lit
                core.append(new_lit)
            for var in vars_head - vars_body:
                core.insert(0, -Term('#', var.type, var))
                # raise InvalidLanguage('Core rule is not range-restricted.')
            cores.append(Clause.from_list(core))

        from problog.logic import Clause as ClauseP
        background = []
        for pred, args in types.items():
            pred = pred.rsplit('/', 1)[0]
            for i, a in enumerate(args):
                argl = [None] * len(args)
                argl[i] = 0
                background.append(ClauseP(Term('#', a, 0), Term(pred, *argl)))

        # Verify the extracted information:
        #  - we need at least one core
        if not cores:
            raise InvalidLanguage('At least one core is required.')
        #  - we need at least one mode
        if not modes:
            raise InvalidLanguage('At least one mode is required.')
        #  - only + and c allowed in positive modes
        for pn, mode in modes:
            if pn:
                for arg in mode.args:
                    if cls.mode_is_add(arg):
                        raise InvalidLanguage('New variables are not allowed in head of clause: \'%s\'.' % mode)
        #  - we need types for all modes
        missing_types = []
        for _, mode in modes:
            if mode.signature not in types:
                missing_types.append(mode.signature)
        if missing_types:
            raise InvalidLanguage('Types are missing for %s.' % and_join(missing_types))

        #  - when a 'c' mode is used, we need constants for that type
        missing_constants = set()
        for cs, ci in constant_modes:
            argtype = types[cs][ci]
            if argtype not in constants:
                missing_constants.add(argtype)
        if missing_constants:
            raise InvalidLanguage('Constants are missing for type %s.' % and_join(missing_constants))

        return CModeLanguage(cores, modes, types, constants, background)