def transform(self, atom): loc = atom.location false = self.__false_atom(loc) atom, ranges = transform_theory_atom(atom) variables = get_variables(atom) param = time_parameter(loc) shift = _ast.Variable(loc, g_tel_shift_variable) aux = self.__aux_atom(loc, variables + [param]) saux = self.__aux_atom(loc, variables + [shift], inc=0) rules = [] if self.__false_external is None: rules.append(_tf.External(loc, false.atom, [])) rules.append(_ast.Rule(loc, atom, [aux])) if ranges: elems = [] for (lhs, rhs), heads in ranges: cond = [] diff = _ast.BinaryOperation(loc, _ast.BinaryOperator.Minus, param, shift) if lhs.ast_type != _ast.ASTType.SymbolicTerm or lhs.symbol.type != _clingo.SymbolType.Number or lhs.symbol.number > 0: cond.append(_ast.Literal(loc, _ast.Sign.NoSign, _ast.Comparison(_ast.ComparisonOperator.LessEqual, lhs, diff))) if rhs.ast_type != _ast.ASTType.SymbolicTerm or rhs.symbol.type != _clingo.SymbolType.Supremum: cond.append(_ast.Literal(loc, _ast.Sign.NoSign, _ast.Comparison(_ast.ComparisonOperator.LessEqual, diff, rhs))) elems.extend([_ast.ConditionalLiteral(loc, _ast.Literal(loc, _ast.Sign.NoSign, head), cond) for head in heads]) rules.append(_ast.Rule(loc, _ast.Disjunction(loc, elems), [saux, false])) return aux, rules
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
def _translate_to_fired_holds(rule_ast, control, builder, t_option): """ Translate the different possible xclingo rules their clingo version making use of 'fired_' and 'holds_' prefixes, then it adds them to the base program using the given builder object. Also it keep trace (inside the given control object) of the rule information that is necessary for computing the causes after the solving phase. @param ast.AST rule_ast: the AST from which the rules will be generated @param clingo.ProgramBuilder builder: builder of the clingo control object that will receive the generated rules. @param bool t_option: if enabled, the function will print the translated sentences but they will not be added to the builder. @return: None """ rule_ast = XClingoAST(rule_ast) if rule_ast.type == ast.ASTType.Rule: # show_all rules, trace_all rules and constraints rules. if rule_ast.is_show_all_rule() or rule_ast.is_trace_all_rule( ) or rule_ast.is_constraint(): if rule_ast['body']: rule_ast.add_prefix("holds_") translated_rule = str(rule_ast) else: translated_rule = str(rule_ast['head']) + "." generated_rules = translated_rule + "\n" else: # Other cases rule_counter = control.count_rule() # Separates the &label literals in the body from the rest label_body, rest_body = _separate_labels_from_body( rule_ast['body']) # Binds the function in the head to a variable to simplify following code head_function = rule_ast['head'].get_function() # Keep trace of head, arguments and body of the rules using rule_counter fired_head_variables = list(map(str, head_function['arguments'])) + \ list(set(map(str, body_variables(rule_ast['body']))) - set(map(str, head_function['arguments']))) control.traces[rule_counter] = { 'head': (rule_ast['head']['atom']['term'].type != ast.ASTType.UnaryOperation, str(head_function['name']), head_function['arguments']), 'arguments': fired_head_variables, # 'body' contains pairs of function names and arguments found in the body 'body': [(lit['atom']['term'].type != ast.ASTType.UnaryOperation, lit.get_function()['name'], lit.get_function()['arguments']) for lit in rest_body if lit.type == ast.ASTType.Literal and lit['atom'].type == ast.ASTType.SymbolicAtom and lit['sign'] == ast.Sign.NoSign] } # Generates fired rule fired_head = "fired_{counter}({arguments})".format( counter=str(rule_counter), arguments=",".join(fired_head_variables)) if rest_body: for a in rest_body: a.add_prefix('holds_') fired_rule = "{fired_head} :- {body}.".format( fired_head=fired_head, body=",".join(map(str, rest_body))) else: fired_rule = fired_head + "." # Generates label rules label_rules = "" for label_ast in label_body: label_rules += "&trace{{{fired_id},{original_head},{label_parameters}}} :- {body}.\n".format( fired_id=str(rule_counter), original_head=str(rule_ast['head']), label_parameters=",".join( [str(e) for e in label_ast['atom']['elements']]), body=fired_head) # Generates holds rule rule_ast['head'].add_prefix('holds_') head_function['arguments'] = [ ast.Variable( v['location'], "Aux" + str(head_function['arguments'].index(v._internal_ast))) for v in head_function['arguments'] ] holds_rule = "{head} :- fired_{rule_counter}({fired_arguments}).".format( head=rule_ast['head'], rule_counter=rule_counter, fired_arguments=",".join([ "Aux" + str(i) for i in range(0, len(fired_head_variables)) ])) # Generates a comment comment = "%" + str(rule_ast) generated_rules = comment + "\n" + fired_rule + "\n" + holds_rule + "\n" + label_rules _add_to_base(generated_rules, builder, t_option)