Beispiel #1
0
 def visit_TheoryAtom(self, atom):
     """
     Rewrites theory atoms related to parity constraints.
     """
     if atom.term.type == _ast.ASTType.Function and len(
             atom.term.arguments) == 0:
         if atom.term.name in ["odd", "even"]:
             self.__remove = True
             i = _ast.Symbol(atom.location, _clingo.Number(self.__id))
             ct = _ast.Symbol(atom.location,
                              _clingo.Function(atom.term.name))
             head = _ast.SymbolicAtom(
                 _ast.Function(atom.location, g_aux_name, [i, ct], False))
             head = _ast.Literal(atom.location, _ast.Sign.NoSign, head)
             self.__add(_ast.Rule(atom.location, head, []))
             for element in atom.elements:
                 head = _ast.Function(
                     atom.location, "",
                     [theory_term_to_term(t) for t in element.tuple], False)
                 head = _ast.SymbolicAtom(
                     _ast.Function(atom.location, g_aux_name, [i, ct, head],
                                   False))
                 head = _ast.Literal(atom.location, _ast.Sign.NoSign, head)
                 body = element.condition
                 self.__add(_ast.Rule(atom.location, head, body))
             self.__id += 1
     return atom
Beispiel #2
0
    def visit_TheoryAtom(self, atom):
        """
        Rewrites theory atoms related to temporal formulas.

        An atom of form `&tel {...}` is rewritten to `&tel(k) {...}`, atoms of
        form `&initial` and `&final` are rewritten to `__initial` and
        `__final`, and atoms of form `&true` and `&false` are rewritten to
        `#true` and `#false`.
        """
        if atom.term.ast_type == _ast.ASTType.Function and len(
                atom.term.arguments) == 0:
            time = lambda loc: _ast.SymbolicTerm(
                loc, _clingo.Function(_tf.g_time_parameter_name))
            wrap = lambda loc, atom: _ast.Literal(
                loc, _ast.Sign.DoubleNegation, atom) if self.__head else atom
            if atom.term.name == "del":
                if not self.__negation and not self.__constraint:
                    raise RuntimeError(
                        "dynamic formulas not supported in this context: {}".
                        format(_tf.str_location(atom.location)))
                atom.term.arguments = [
                    _ast.SymbolicTerm(atom.term.location,
                                      _clingo.Function("__t"))
                ]
            elif atom.term.name == "tel":
                if self.__head:
                    atom, rules = self.__head_transformer.transform(atom)
                    self.__aux_rules.extend(rules)
                else:
                    if not self.__negation and not self.__constraint:
                        raise RuntimeError(
                            "temporal formulas not supported in this context: {}"
                            .format(_tf.str_location(atom.location)))
                    for element in atom.elements:
                        if len(element.terms) != 1:
                            raise RuntimeError(
                                "invalid temporal formula: {}".format(
                                    _tf.str_location(atom.location)))
                        self.visit(element.condition)
                    atom.term = self.__term_transformer.visit(
                        atom.term, False, True, True, self.__max_shift)
            elif atom.term.name == "initial":
                atom = wrap(
                    atom.location,
                    _ast.SymbolicAtom(
                        _ast.Function(atom.location, "__initial",
                                      [time(atom.location)], False)))
            elif atom.term.name == "final":
                atom = wrap(
                    atom.location,
                    _ast.SymbolicAtom(
                        _ast.Function(atom.location, "__final",
                                      [time(atom.location)], False)))
            elif atom.term.name == "true":
                atom = wrap(atom.location, _ast.BooleanConstant(True))
            elif atom.term.name == "false":
                atom = wrap(atom.location, _ast.BooleanConstant(False))
        return atom
 def visit_TheoryAtom(self, atom: AST, in_lit: bool = False) -> AST:
     '''
     Visit theory atom and tag as given by in_lit.
     '''
     # pylint: disable=invalid-name,no-self-use
     term = atom.term
     if term.name == "diff" and not term.arguments:
         loc = "body" if in_lit else "head"
         atom.term = ast.Function(
             term.location, term.name,
             [ast.Function(term.location, loc, [], False)], False)
     return atom
Beispiel #4
0
 def __aux_atom(self, location, variables, inc=1):
     self.__num_aux += inc
     return _ast.Literal(
         location, _ast.Sign.NoSign,
         _ast.SymbolicAtom(
             _ast.Function(location, "__aux_{}".format(self.__num_aux - 1),
                           variables, False)))
Beispiel #5
0
 def visit_TheoryFunction(self, x):
     """
     Theory functions are mapped to functions.
     """
     isnum = lambda y: y.type == _ast.ASTType.Symbol and y.symbol.type == _clingo.SymbolType.Number
     if x.name == "-" and len(x.arguments) == 1:
         rhs = self(x.arguments[0])
         if isnum(rhs):
             return _ast.Symbol(x.location, _clingo.Number(-rhs.symbol.number))
         else:
             return _ast.UnaryOperation(x.location, _ast.UnaryOperator.Minus, rhs)
     elif (x.name == "+" or x.name == "-") and len(x.arguments) == 2:
         lhs = self(x.arguments[0])
         rhs = self(x.arguments[1])
         op  = _ast.BinaryOperator.Plus if x.name == "+" else _ast.BinaryOperator.Minus
         if isnum(lhs) and isnum(rhs):
             lhs = lhs.symbol.number
             rhs = rhs.symbol.number
             return _ast.Symbol(x.location, _clingo.Number(lhs + rhs if x.name == "+" else lhs - rhs))
         else:
             return _ast.BinaryOperation(x.location, op, lhs, rhs)
     elif x.name == "-" and len(x.arguments) == 2:
         return _ast.BinaryOperation(x.location, _ast.BinaryOperator.Minus, self(x.arguments[0]), self(x.arguments[1]))
     elif (x.name, TheoryParser.binary) in TheoryParser.table or (x.name, TheoryParser.unary) in TheoryParser.table:
         raise RuntimeError("operator not handled: {}".format(str_location(x.location)))
     else:
         return _ast.Function(x.location, x.name, [self(a) for a in x.arguments], False)
Beispiel #6
0
 def __append_final(self, x, param=None):
     loc = x.location
     fun = _ast.Function(
         loc, "__final",
         [_ast.SymbolicTerm(loc, param)] if param is not None else [],
         False)
     x.body.append(
         _ast.Literal(loc, _ast.Sign.NoSign, _ast.SymbolicAtom(fun)))
Beispiel #7
0
 def visit_TheoryTermSequence(self, x):
     """
     Theory term tuples are mapped to term tuples.
     """
     if x.sequence_type == _ast.TheorySequenceType.Tuple:
         return _ast.Function(x.location, "", [self(a) for a in x.arguments], False)
     else:
         raise RuntimeError("invalid term: {}".format(str_location(x.location)))
Beispiel #8
0
 def add_part(part_name, atom_name, statement, wrap=lambda x: x):
     params = [
         _ast.Id(loc, _tf.g_time_parameter_name),
         _ast.Id(loc, _tf.g_time_parameter_name_alt)
     ]
     callback(_ast.Program(loc, part_name, params))
     atom = wrap(
         _ast.SymbolicAtom(_ast.Function(loc, atom_name, [time], False)))
     callback(statement(loc, atom, []))
Beispiel #9
0
    def visit_TheoryAtom(self, x):
        """
        Transforms the given theory atom to be processed further.

        The theory atom is renamed from tel to tel_head(__t) and the
        """
        if x.guard is not None:
            raise RuntimeError("invalid temporal formula in rule head: {}".format(_tf.str_location(x.location)))
        x.term     = _ast.Function(x.term.location, "__tel_head", [time_parameter(x.term.location)], False)
        x.elements = [self(elem) for elem in x.elements]
        return x
Beispiel #10
0
 def visit(self, x, *args, **kwargs):
     ret = super().visit(x, *args, **kwargs)
     if self.final and hasattr(ret, "body"):
         if x is ret:
             ret = copy(x)
         loc = ret.location
         fun = ast.Function(loc, "finally",
                            [ast.SymbolicTerm(loc, self.parameter)], False)
         atm = ast.SymbolicAtom(fun)
         lit = ast.Literal(loc, ast.Sign.NoSign, atm)
         ret.body.append(lit)
     return ret
Beispiel #11
0
    def _get_unsat_atoms(self, location: dict, idx):
        """
        Creates the 'unsat' and 'not unsat' atoms
        """
        unsat_arguments = [idx, self.weight, self.global_variables]

        unsat = ast.SymbolicAtom(
            ast.Function(location, "unsat", unsat_arguments, False))

        not_unsat = ast.Literal(location, ast.Sign.Negation, unsat)
        unsat = ast.Literal(location, ast.Sign.NoSign, unsat)

        return unsat, not_unsat
Beispiel #12
0
    def visit_TheoryAtom(self, atom, loc="body"):
        """
        Mark head/body literals and ensure multiset semantics for theory atoms.
        """
        term = atom.term

        # ensure multi set semantics for theory atoms
        if term.name in [
                "sum", "diff", "in", "max", "min", "count", "distinct",
                "minimize", "maximize"
        ] and not term.arguments:
            atom = unpool_theory_atom(atom)
            atom.elements = self._rewrite_tuples(atom.elements)

        # annotate theory atoms in heads and bodies
        if term.name in ["sum", "diff", "in", "max", "min", "count"
                         ] and not term.arguments:
            atom.term = ast.Function(
                term.location, term.name,
                [ast.Function(term.location, loc, [], False)], False)

        return atom
Beispiel #13
0
    def __atom(self, location, positive, name, arguments):
        """
        Helper function to create an atom.

        Arguments:
        location --  Location to use.
        positive --  Classical sign of the atom.
        name     --  The name of the atom.
        arguments -- The arguments of the atom.
        """
        ret = _ast.Function(location, name, arguments + [time_parameter(location)], False)
        if not positive:
            ret = _ast.UnaryOperation(location, _ast.UnaryOperator.Minus, ret)
        return _ast.SymbolicAtom(ret)
Beispiel #14
0
 def visit_Literal_in_Head_Disjuntion(self, x, *args, **kwargs):
     if x.sign == ast.Sign.DoubleNegation:
         sign = self.__dnegative_term
     elif x.sign == ast.Sign.Negation:
         sign = self.__negative_term
     else:
         sign = self.__positive_term
     rule_id = ast.Symbol(x.location, self.__rule_id_number)
     sign = ast.Symbol(x.location, sign)
     fun = ast.Function(x.location, auxiliary_atom_name,
                        [rule_id, sign, x.atom.term], False)
     new_literal = ast.Literal(x.location, ast.Sign.NoSign,
                               ast.SymbolicAtom(fun))
     self.__auxiliary_atoms.append(new_literal)
     rule = ast.Rule(x.location, head=x, body=[new_literal])
     self.__auxiliary_rules.append(rule)
     return new_literal
Beispiel #15
0
    def _get_constraint_parameters(self, location: dict):
        """
        Get the correct parameters for the
        weak constraint in the conversion.
        """
        idx = ast.SymbolicTerm(location, Number(self.rule_idx))
        self.global_variables = ast.Function(location, "",
                                             self.global_variables, False)

        if self.weight == 'alpha':
            self.weight = ast.SymbolicTerm(location, String('alpha'))
            constraint_weight = ast.SymbolicTerm(location, Number(1))
            priority = Number(1)
        else:
            constraint_weight = self.weight
            priority = Number(0)
        priority = ast.SymbolicTerm(location, priority)
        return idx, constraint_weight, priority
Beispiel #16
0
 def visit_Function(self, x):
     """
     Function(location: Location, name: str, arguments: term*, external: bool)
     """
     return [ast.Function(x.location, x.name, args, x.external)
             for args in unpool_list_with(self.visit, x.arguments)]
Beispiel #17
0
 def __false_atom(self, location):
     return _ast.Literal(
         location, _ast.Sign.NoSign,
         _ast.SymbolicAtom(
             _ast.Function(location, g_tel_false_atom,
                           [time_parameter(location)], False)))
Beispiel #18
0
        This function accepts additional positional and keyword arguments,
        which are passed to node-specific visit functions and to the visit
        function called for child nodes.
        """
        if hasattr(x, "type"):
            attr = "visit_" + str(x.type)
            if hasattr(self, attr):
                return getattr(self, attr)(x, *args, **kwargs)
            else:
                return self.visit_children(x, *args, **kwargs)
        elif isinstance(x, list):
            return [self.visit(y, *args, **kwargs) for y in x]
        elif x is None:
            return x
        else:
            raise TypeError("unexpected type")

    def __call__(self, x, *args, **kwargs):
        """
        Alternative way to call visit.
        """
        return self.visit(x, *args, **kwargs)


_version = _clingo.__version__.split(".")
if int(_version[0]) >= 5 and int(_version[1]) >= 4:
    External = lambda loc, head, body: _ast.External(
        loc, head, body, _ast.Function(loc, "false", [], False))
else:
    External = _ast.External
Beispiel #19
0
def transform(inputs, callback):
    """
    Transforms the given list of temporal programs in string form into an ASP
    program.

    Returns the future predicates whose atoms have to be set to false if
    referring to the future, and program parts that have to be regrounded if
    there are constraints referring to the future.

    Arguments:
    inputs   -- The list of inputs.
    callback -- Callback for rewritten statements.
    """
    loc = {
        'begin': {
            'line': 1,
            'column': 1,
            'filename': '<transform>'
        },
        'end': {
            'line': 1,
            'column': 1,
            'filename': '<transform>'
        }
    }
    future_predicates = set()
    constraint_parts = {}
    time = _ast.Symbol(loc, _clingo.Function(_tf.g_time_parameter_name))
    wrap_lit = lambda a: _ast.Literal(loc, _ast.Sign.NoSign, a)

    # apply transformer to program
    def append(s):
        if s is not None:
            callback(s)

    aux_rules = []
    transformer = _prg.ProgramTransformer(future_predicates, constraint_parts,
                                          aux_rules)
    for i in inputs:
        _clingo.parse_program(i, lambda s: append(transformer.visit(s)))
    if aux_rules:
        callback(
            _ast.Program(loc, "always", [
                _ast.Id(loc, _tf.g_time_parameter_name),
                _ast.Id(loc, _tf.g_time_parameter_name_alt)
            ]))
        for rule in aux_rules:
            callback(rule)

    # add auxiliary rules for future predicates
    future_sigs = []
    if len(future_predicates) > 0:
        callback(
            _ast.Program(loc, "always", [
                _ast.Id(loc, _tf.g_time_parameter_name),
                _ast.Id(loc, _tf.g_time_parameter_name_alt)
            ]))
        for name, arity, positive, shift in sorted(future_predicates):
            variables = [
                _ast.Variable(loc, "{}{}".format(_tf.g_variable_prefix, i))
                for i in range(arity)
            ]
            s = _ast.Symbol(loc, _clingo.Number(shift))
            t_shifted = _ast.BinaryOperation(loc, _ast.BinaryOperator.Plus,
                                             time, s)
            add_sign = lambda lit: lit if positive else _ast.UnaryOperation(
                loc, _ast.UnaryOperator.Minus, lit)
            p_current = _ast.SymbolicAtom(
                add_sign(_ast.Function(loc, name, variables + [time], False)))
            f_current = _ast.SymbolicAtom(
                add_sign(
                    _ast.Function(loc, _tf.g_future_prefix + name,
                                  variables + [s, time], False)))
            callback(_ast.Rule(loc, wrap_lit(p_current),
                               [wrap_lit(f_current)]))
            future_sigs.append(
                (_tf.g_future_prefix + name, arity + 2, positive))

    # gather rules for constraints referring to the future
    reground_parts = []
    if len(constraint_parts) > 0:
        for (name, shift), rules in constraint_parts.items():
            assert (shift > 0)
            params = [
                _ast.Id(loc, _tf.g_time_parameter_name),
                _ast.Id(loc, _tf.g_time_parameter_name_alt)
            ]
            # parts to be regrounded
            part = "{}_0_{}".format(name, shift - 1)
            callback(_ast.Program(loc, part, params))
            for p, l in rules:
                callback(p)
            reground_parts.append((name, part, range(shift)))
            # parts that no longer have to be regrounded
            last_part = "{}_{}".format(name, shift)
            callback(_ast.Program(loc, last_part, params))
            for p, l in rules:
                callback(l)
            reground_parts.append((name, last_part, range(shift, shift + 1)))

    def add_part(part_name, atom_name, statement, wrap=lambda x: x):
        params = [
            _ast.Id(loc, _tf.g_time_parameter_name),
            _ast.Id(loc, _tf.g_time_parameter_name_alt)
        ]
        callback(_ast.Program(loc, part_name, params))
        atom = wrap(
            _ast.SymbolicAtom(_ast.Function(loc, atom_name, [time], False)))
        callback(statement(loc, atom, []))

    add_part('initial', '__initial', _ast.Rule, wrap_lit)
    add_part('always', '__final', _tf.External)

    reground_parts.append(('always', 'always', range(1)))
    reground_parts.append(('dynamic', 'dynamic', range(1)))
    reground_parts.append(('initial', 'initial', range(1)))

    def no_program(s):
        if s.type != _ast.ASTType.Program:
            callback(s)

    _clingo.parse_program(
        _dedent('''\
        #theory tel {
            formula_body  {
                &   : 7, unary;         % prefix for keywords
                -   : 7, unary;         % classical negation
                +   : 6, binary, left;  % arithmetic +
                -   : 6, binary, left;  % arithmetic -
                ~   : 5, unary;         % negation
                <   : 5, unary;         % previous
                <   : 5, binary, right; % n x previous
                <:  : 5, unary;         % weak previous
                <:  : 5, binary, right; % n x weak previous
                <?  : 5, unary;         % eventually-
                <*  : 5, unary;         % always-
                <<  : 5, unary;         % initially
                >   : 5, unary;         % next
                >   : 5, binary, right; % n x next
                >:  : 5, unary;         % weak next
                >:  : 5, binary, right; % n x weak next
                >?  : 5, unary;         % eventually+
                >*  : 5, unary;         % always+
                >>  : 5, unary;         % finally
                >*  : 4, binary, left;  % release
                >?  : 4, binary, left;  % until
                <*  : 4, binary, left;  % trigger
                <?  : 4, binary, left;  % since
                &   : 3, binary, left;  % and
                |   : 2, binary, left;  % or
                <-  : 1, binary, left;  % left implication
                ->  : 1, binary, left;  % right implication
                <>  : 1, binary, left;  % equivalence
                ;>  : 0, binary, right; % sequence next
                ;>: : 0, binary, right; % sequence weak next
                <;  : 0, binary, left;  % sequence previous
                <:; : 0, binary, left   % sequence weak previous
            };
            formula_head  {
                &   : 7, unary;         % prefix for keywords
                -   : 7, unary;         % classical negation
                +   : 6, binary, left;  % arithmetic +
                -   : 6, binary, left;  % arithmetic -
                ~   : 5, unary;         % negation
                >   : 5, unary;         % next
                >   : 5, binary, right; % n x next
                >:  : 5, unary;         % weak next
                >:  : 5, binary, right; % n x weak next
                >?  : 5, unary;         % eventually+
                >*  : 5, unary;         % always+
                >>  : 5, unary;         % finally
                >*  : 4, binary, left;  % release
                >?  : 4, binary, left;  % until
                &   : 3, binary, left;  % and
                |   : 2, binary, left;  % or
                ;>  : 0, binary, right; % sequence next
                ;>: : 0, binary, right  % sequence weak next
            };
            &tel/1 : formula_body, body;
            &__tel_head/1 : formula_body, head
        }.
        '''), no_program)

    _clingo.parse_program(
        _dedent('''\
        #theory del {
            formula_body  {
                &   : 7, unary;         % prefix for keywords
                ?   : 4, unary;         % check
                *   : 3, unary;         % kleene star
                +   : 2, binary, left;  % choice
                ;;  : 1, binary, left;  % sequence
                .>? : 0, binary, right; % diamond (eventually)
                .>* : 0, binary, right  % box (always)
            };
            &del/1 : formula_body, body
        }.
        '''), no_program)

    return future_sigs, reground_parts
Beispiel #20
0
    def _convert_rule(self, head, body):
        """
        Converts the LPMLN rule using either the unsat atoms
        or the simplified approach without them (default setting)
        """
        loc = head.location
        idx, constraint_weight, priority = self._get_constraint_parameters(loc)

        # Choice rules without bound can be skipped
        if str(head.ast_type) == 'ASTType.Aggregate':
            if head.left_guard is None and head.right_guard is None:
                return [ast.Rule(loc, head, body)]
            else:
                not_head = ast.Literal(loc, ast.Sign.Negation, head)

        else:
            not_head = ast.Literal(loc, ast.Sign.Negation, head.atom)

        # Create ASP rules
        # TODO: Better way to insert and delete items from body?
        if self.use_unsat:
            unsat, not_unsat = self._get_unsat_atoms(loc, idx)
            # Rule 1 (unsat :- Body, not Head)
            body.insert(0, not_head)
            asp_rule1 = ast.Rule(loc, unsat, body)

            # Rule 2 (Head :- Body, not unsat)
            del body[0]
            body.insert(0, not_unsat)
            asp_rule2 = ast.Rule(loc, head, body)

            # Rule 3 (weak constraint unsat)
            asp_rule3 = ast.Minimize(loc, constraint_weight, priority,
                                     [idx, self.global_variables], [unsat])
            return [asp_rule1, asp_rule2, asp_rule3]
        else:
            asp_rules = []
            # Choice rules with bounds, e.g. 'w : { a; b } = 1 :- B.'
            # get converted to two rules:
            # w : { a ; b } :- B.       --> { a ; b } :- B.
            # w : :- not { a ; b } = 1. --> :~ B, not {a ; b} = 1. [w,id, X]
            if str(head.ast_type) == 'ASTType.Aggregate':
                agg1 = ast.Aggregate(loc, None, head.elements, None)
                asp_rules.append(ast.Rule(loc, agg1, body))
                body.insert(0, not_head)
            # Convert integrity constraint 'w: #false :- B.' to weak constraint
            # of form: ':~ B. [w, idx, X]'
            elif str(head.atom.ast_type
                     ) == 'ASTType.BooleanConstant' and not head.atom.value:
                pass
            # Convert normal rule 'w: H :- B.' to choice rule and weak
            # constraint of form: '{H} :- B.' and ':~ B, not H. [w, idx, X]'
            else:
                cond_head = ast.ConditionalLiteral(loc, head, [])
                choice_head = ast.Aggregate(loc, None, [cond_head], None)
                asp_rules.append(ast.Rule(loc, choice_head, body))
                body.insert(0, not_head)

            # TODO: Should the two solve calls work with unsat as well?
            if self.two_solve_calls and str(priority) == '0':
                ext_helper_atom = ast.SymbolicAtom(
                    ast.Function(loc, 'ext_helper', [], False))
                ext_helper_atom = ast.Literal(loc, ast.Sign.NoSign,
                                              ext_helper_atom)
                body.insert(0, ext_helper_atom)

            weak_constraint = ast.Minimize(loc, constraint_weight, priority,
                                           [idx, self.global_variables], body)
            asp_rules.append(weak_constraint)
            return asp_rules
Beispiel #21
0
        returned. Otherwise, its children are visited and transformed.

        This function accepts additional positional and keyword arguments,
        which are passed to node-specific visit functions and to the visit
        function called for child nodes.
        """
        if hasattr(x, "ast_type"):
            attr = "visit_" + str(x.ast_type)
            if hasattr(self, attr):
                return getattr(self, attr)(x, *args, **kwargs)
            else:
                return self.visit_children(x, *args, **kwargs)
        elif isinstance(x, list):
            return [self.visit(y, *args, **kwargs) for y in x]
        elif x is None:
            return x
        else:
            raise TypeError("unexpected type")

    def __call__(self, x, *args, **kwargs):
        """
        Alternative way to call visit.
        """
        return self.visit(x, *args, **kwargs)

_version = _clingo.__version__.split(".")
if int(_version[0]) >= 5 and int(_version[1]) >= 4:
    External = lambda loc, head, body: _ast.External(loc, head, body, _ast.Function(loc, "false", [], False))
else:
    External = _ast.External