def remove_duration_variable(task): def recurse(condition, act, time, duration, pnes): if isinstance(condition, pddl.FunctionComparison): parts = [ exp.remove_duration_variable(act, time, duration, pnes) for exp in condition.parts ] return condition.__class__(condition.comparator, parts) # return pddl.FunctionComparison(condition.comparator,parts) else: new_parts = [ recurse(part, act, time, duration, pnes) for part in condition.parts ] return condition.change_parts(new_parts) for act in task.durative_actions: assert len(act.duration[1]) == 0, "at end durations are not supported" assert len(act.duration[0]) == 1 and act.duration[0][0][0] == "=" duration = act.duration[0][0][1] duration_functions = [] # remove from action conditions condition = [] for time, cond in enumerate(act.condition): condition.append( recurse(cond, act, time, duration, duration_functions)) act.condition = condition for time in range(2): for eff in act.effects[time]: # remove from effect condition condition = [] for eff_time, cond in enumerate(eff.condition): condition.append( recurse(cond, act, eff_time, duration, duration_functions)) eff.condition = condition # remove from effect if isinstance(eff.peffect, pddl.FunctionAssignment): assign = eff.peffect assign.expression = assign.expression.remove_duration_variable( act, time, duration, duration_functions) for pne in duration_functions: assign = pddl.Assign(pne, duration) condition = [pddl.Truth(), pddl.Truth(), pddl.Truth()] effect = pddl.Effect([], condition, assign) act.effects[0].append(effect) task.function_symbols[pne.symbol] = "number"
def add_effect(tmp_effect, result): """tmp_effect has the following structure: [ConjunctiveEffect] [UniversalEffect] [ConditionalEffect] SimpleEffect.""" if isinstance(tmp_effect, pddl.ConjunctiveEffect): for effect in tmp_effect.effects: add_effect(effect, result) return else: parameters = [] condition = pddl.Truth() if isinstance(tmp_effect, pddl.UniversalEffect): parameters = tmp_effect.parameters if isinstance(tmp_effect.effect, pddl.ConditionalEffect): condition = tmp_effect.effect.condition assert isinstance(tmp_effect.effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect.effect else: assert isinstance(tmp_effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect elif isinstance(tmp_effect, pddl.ConditionalEffect): condition = tmp_effect.condition assert isinstance(tmp_effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect else: assert isinstance(tmp_effect, pddl.SimpleEffect) effect = tmp_effect.effect assert isinstance(effect, pddl.Literal) # Check for contradictory effects condition = condition.simplified() new_effect = pddl.Effect(parameters, condition, effect) contradiction = pddl.Effect(parameters, condition, effect.negate()) ### REMOVED CONTRADICTION CHECK result.append(new_effect)
def convert_condition(condition): import pddl class_name = condition.__class__.__name__ if class_name in ('Truth', 'FunctionComparison'): # TODO: currently ignoring numeric conditions return pddl.Truth() elif class_name == 'Atom': return pddl.Atom(condition.predicate, convert_args(condition.args)) elif class_name == 'NegatedAtom': return pddl.NegatedAtom(condition.predicate, convert_args(condition.args)) elif class_name == 'Conjunction': return pddl.conditions.Conjunction( list(map(convert_condition, condition.parts))) elif class_name == 'Disjunction': return pddl.Disjunction(list(map(convert_condition, condition.parts))) elif class_name == 'ExistentialCondition': return pddl.ExistentialCondition( convert_parameters(condition.parameters), list(map(convert_condition, condition.parts))) elif class_name == 'UniversalCondition': return pddl.UniversalCondition( convert_parameters(condition.parameters), list(map(convert_condition, condition.parts))) raise NotImplementedError(class_name)
def get_problem(evaluations, goal_exp, domain, unit_costs=False): objects = objects_from_evaluations(evaluations) typed_objects = list( {make_object(pddl_from_object(obj)) for obj in objects} - set(domain.constants)) # TODO: this doesn't include = init = [ fd_from_evaluation(e) for e in evaluations if not is_negated_atom(e) ] goal = pddl.Truth() if goal_exp is None else parse_goal(goal_exp, domain) problem_pddl = None if USE_FORBID: problem_pddl = get_problem_pddl(evaluations, goal_exp, domain.pddl, temporal=False) write_pddl(domain.pddl, problem_pddl) return Problem(task_name=domain.name, task_domain_name=domain.name, objects=sorted(typed_objects, key=lambda o: o.name), task_requirements=pddl.tasks.Requirements([]), init=init, goal=goal, use_metric=not unit_costs, pddl=problem_pddl)
def get_stream_action(result, name, unit_cost, effect_scale=1): #from pddl_parser.parsing_functions import parse_action import pddl parameters = [] preconditions = [ fd_from_fact(fact) for fact in result.instance.get_domain() ] precondition = pddl.Conjunction(preconditions) effects = [ pddl.Effect(parameters=[], condition=pddl.Truth(), literal=fd_from_fact(fact)) for fact in result.get_certified() ] effort = 1 if unit_cost else result.instance.get_effort() if effort == INF: return None fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]) expression = pddl.NumericConstant(int_ceil(effect_scale * effort)) # Integer cost = pddl.Increase(fluent=fluent, expression=expression) # Can also be None return pddl.Action(name=name, parameters=parameters, num_external_parameters=len(parameters), precondition=precondition, effects=effects, cost=cost)
def get_necessary_axioms(instance, axioms, negative_from_name): import pddl axioms_from_name = get_derived_predicates(axioms) atom_queue = [] processed_atoms = set() def add_literals(literals): for literal in literals: atom = literal.positive() if atom not in processed_atoms: atom_queue.append(literal.positive()) processed_atoms.add(atom) add_literals(instance.precondition) for (cond, _) in (instance.add_effects + instance.del_effects): add_literals(cond) axiom_from_action = {} partial_instantiations = set() while atom_queue: literal = atom_queue.pop() for axiom in axioms_from_name[literal.predicate]: derived_parameters = axiom.parameters[:axiom. num_external_parameters] var_mapping = { p.name: a for p, a in zip(derived_parameters, literal.args) if a[0] != '?' } key = (axiom, frozenset(var_mapping.items())) if key in partial_instantiations: continue partial_instantiations.add(key) parts = [] for literal in get_literals(axiom.condition): if literal.predicate in negative_from_name: continue parts.append(literal.rename_variables(var_mapping)) # new_condition = axiom.condition.uniquify_variables(None, var_mapping) effect_args = [ var_mapping.get(a.name, a.name) for a in derived_parameters ] effect = pddl.Effect([], pddl.Truth(), pddl.conditions.Atom(axiom.name, effect_args)) free_parameters = [ p for p in axiom.parameters if p.name not in var_mapping ] new_action = pddl.Action(axiom.name, free_parameters, 0, pddl.Conjunction(parts), [effect], None) # Creating actions so I can partially instantiate (impossible with axioms) axiom_from_action[new_action] = (axiom, var_mapping) add_literals(parts) return axiom_from_action
def get_necessary_axioms(conditions, axioms, negative_from_name): if not conditions or not axioms: return {} axioms_from_name = get_derived_predicates(axioms) atom_queue = [] processed_atoms = set() def add_literals(literals): for lit in literals: atom = lit.positive() if atom not in processed_atoms: atom_queue.append( atom) # Previously was lit.positive() for some reason? processed_atoms.add(atom) import pddl add_literals(conditions) axiom_from_action = {} partial_instantiations = set() while atom_queue: literal = atom_queue.pop() for axiom in axioms_from_name[literal.predicate]: derived_parameters = axiom.parameters[:axiom. num_external_parameters] var_mapping = { p.name: a for p, a in zip(derived_parameters, literal.args) if not is_parameter(a) } key = (axiom, frozenset(var_mapping.items())) if key in partial_instantiations: continue partial_instantiations.add(key) parts = [ l.rename_variables(var_mapping) for l in get_literals(axiom.condition) if l.predicate not in negative_from_name ] # Assumes a conjunction? # new_condition = axiom.condition.uniquify_variables(None, var_mapping) effect_args = [ var_mapping.get(a.name, a.name) for a in derived_parameters ] effect = pddl.Effect([], pddl.Truth(), pddl.conditions.Atom(axiom.name, effect_args)) free_parameters = [ p for p in axiom.parameters if p.name not in var_mapping ] new_action = pddl.Action(axiom.name, free_parameters, len(free_parameters), pddl.Conjunction(parts), [effect], None) # Creating actions so I can partially instantiate (impossible with axioms) axiom_from_action[new_action] = (axiom, var_mapping) add_literals(parts) return axiom_from_action
def add_effect(tmp_effect, result): """tmp_effect has the following structure: [ConjunctiveEffect] [UniversalEffect] [ConditionalEffect] SimpleEffect.""" if isinstance(tmp_effect, pddl.ConjunctiveEffect): for effect in tmp_effect.effects: add_effect(effect, result) return else: parameters = [] condition = pddl.Truth() if isinstance(tmp_effect, pddl.UniversalEffect): parameters = tmp_effect.parameters if isinstance(tmp_effect.effect, pddl.ConditionalEffect): condition = tmp_effect.effect.condition assert isinstance(tmp_effect.effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect.effect else: assert isinstance(tmp_effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect elif isinstance(tmp_effect, pddl.ConditionalEffect): condition = tmp_effect.condition assert isinstance(tmp_effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect else: assert isinstance(tmp_effect, pddl.SimpleEffect) effect = tmp_effect.effect if effect is None: # Nothing to add return assert isinstance(effect, pddl.Literal) # Check for contradictory effects condition = condition.simplified() new_effect = pddl.Effect(parameters, condition, effect) contradiction = pddl.Effect(parameters, condition, effect.negate()) if not contradiction in result: result.append(new_effect) else: # We use add-after-delete semantics, keep positive effect if isinstance(contradiction.literal, pddl.NegatedAtom): result.remove(contradiction) result.append(new_effect)
def compile_to_exogenous_actions(evaluations, domain, streams): import pddl # TODO: automatically derive fluents # TODO: version of this that operates on fluents of length one? # TODO: better instantiation when have full parameters # TODO: conversion from stream cost to real cost units? # TODO: any predicates derived would need to be replaced as well fluent_predicates = get_fluents(domain) domain_predicates = {get_prefix(a) for s in streams for a in s.domain} if not (domain_predicates & fluent_predicates): return certified_predicates = {get_prefix(a) for s in streams for a in s.certified} future_map = {p: 'f-{}'.format(p) for p in certified_predicates} augment_evaluations(evaluations, future_map) rename_future = lambda a: rename_atom(a, future_map) for stream in list(streams): if not isinstance(stream, Stream): raise NotImplementedError(stream) # TODO: could also just have conditions asserting that one of the fluent conditions fails streams.append(create_static_stream(stream, evaluations, fluent_predicates, rename_future)) stream_atom = streams[-1].certified[0] parameters = [pddl.TypedObject(p, 'object') for p in get_args(stream_atom)] # TODO: add to predicates as well? domain.predicate_dict[get_prefix(stream_atom)] = pddl.Predicate(get_prefix(stream_atom), parameters) precondition = pddl.Conjunction(tuple(map(fd_from_fact, (stream_atom,) + tuple(stream.domain)))) effects = [pddl.Effect(parameters=[], condition=pddl.Truth(), literal=fd_from_fact(fact)) for fact in stream.certified] effort = 1 # TODO: use stream info #effort = 1 if unit_cost else result.instance.get_effort() #if effort == INF: # continue fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]) expression = pddl.NumericConstant(int_ceil(effort)) # Integer cost = pddl.Increase(fluent=fluent, expression=expression) # Can also be None domain.actions.append(pddl.Action(name='call-{}'.format(stream.name), parameters=parameters, num_external_parameters=len(parameters), precondition=precondition, effects=effects, cost=cost)) stream.certified = tuple(set(stream.certified) | set(map(rename_future, stream.certified)))
def make_effects(effects): return [ pddl.Effect(parameters=[], condition=pddl.Truth(), literal=fd_from_fact(fact)) for fact in effects ]
def parse_task_pddl(task_pddl, type_dict, predicate_dict): iterator = iter(task_pddl) define_tag = next(iterator) assert define_tag == "define" problem_line = next(iterator) assert problem_line[0] == "problem" and len(problem_line) == 2 yield problem_line[1] domain_line = next(iterator) assert domain_line[0] == ":domain" and len(domain_line) == 2 yield domain_line[1] requirements_opt = next(iterator) if requirements_opt[0] == ":requirements": requirements = requirements_opt[1:] objects_opt = next(iterator) else: requirements = [] objects_opt = requirements_opt yield pddl.Requirements(requirements) if objects_opt[0] == ":objects": yield parse_typed_list(objects_opt[1:]) init = next(iterator) else: yield [] init = objects_opt assert init[0] == ":init" initial = [] initial_true = set() initial_false = set() initial_assignments = dict() for fact in init[1:]: if fact[0] == "=": try: assignment = parse_assignment(fact) except ValueError as e: raise SystemExit("Error in initial state specification\n" + "Reason: %s." % e) if not isinstance(assignment.expression, pddl.NumericConstant): raise SystemExit("Illegal assignment in initial state " + "specification:\n%s" % assignment) if assignment.fluent in initial_assignments: prev = initial_assignments[assignment.fluent] if assignment.expression == prev.expression: print("Warning: %s is specified twice" % assignment, "in initial state specification") else: raise SystemExit("Error in initial state specification\n" + "Reason: conflicting assignment for " + "%s." % assignment.fluent) else: initial_assignments[assignment.fluent] = assignment initial.append(assignment) elif fact[0] == "not": atom = pddl.Atom(fact[1][0], fact[1][1:]) check_atom_consistency(atom, initial_false, initial_true, False) initial_false.add(atom) else: atom = pddl.Atom(fact[0], fact[1:]) check_atom_consistency(atom, initial_true, initial_false) initial_true.add(atom) initial.extend(initial_true) yield initial goal = next(iterator) goal_cond = pddl.Truth() if goal[0] == ":goal": if len(goal) == 2: goal_cond = parse_condition(goal[1], type_dict, predicate_dict) utility = next(iterator) else: utility = goal yield goal_cond assert utility[0] == ":utility" utility_list = [] for fact in utility[1:]: assert fact[0] == "=" utility_atom = pddl.Atom(fact[1][0], fact[1][1:]) utility_value = fact[2] utility_list.append((utility_atom, utility_value)) yield utility_list bound = next(iterator) assert bound[0] == ":bound" and len(bound) == 2 yield bound[1] use_metric = False for entry in iterator: if entry[0] == ":use-cost-metric": use_metric = True # for entry in iterator: # if entry[0] == ":metric": # if entry[1]=="maximize" and entry[2][0] == "total-utility": # use_metric = True # else: # assert False, "Unknown metric." yield use_metric for entry in iterator: assert False, entry
def add_effect(tmp_effect, result): # adds effect from tmp_effect to result """tmp_effect has the following structure: [ConjunctiveEffect] [UniversalEffect] [ConditionalEffect] SimpleEffect.""" if isinstance(tmp_effect, pddl.ConjunctiveEffect): for effect in tmp_effect.effects: add_effect(effect, result) return else: parameters = [] condition = pddl.Truth() if isinstance(tmp_effect, pddl.UniversalEffect): # print ("universal effect: ", tmp_effect) parameters = tmp_effect.parameters if isinstance(tmp_effect.effect, pddl.ConditionalEffect): condition = tmp_effect.effect.condition assert isinstance(tmp_effect.effect.effect, pddl.SimpleEffect)\ or isinstance(tmp_effect.effect.effect, pddl.NumericEffect) effect = tmp_effect.effect.effect.effect else: assert isinstance(tmp_effect.effect, pddl.SimpleEffect)\ or isinstance(tmp_effect.effect, pddl.NumericEffect), "Effect is not primitive but %s" % tmp_effect.effect.effect.__class__ effect = tmp_effect.effect.effect elif isinstance(tmp_effect, pddl.ConditionalEffect): # print ("conditional effect: ", tmp_effect) condition = tmp_effect.condition assert isinstance(tmp_effect.effect, pddl.SimpleEffect) effect = tmp_effect.effect.effect elif isinstance(tmp_effect, pddl.SimpleEffect): # print ("simple effect: ", tmp_effect) effect = tmp_effect.effect assert isinstance(effect, pddl.Literal) else: # print ("numeric effect: ", tmp_effect) effect = tmp_effect.effect assert isinstance(effect, pddl.FunctionAssignment) # Check for contradictory effects condition = condition.simplified() new_effect = pddl.Effect(parameters, condition, effect) if isinstance(effect, pddl.FunctionAssignment): # The check for multiple effects on the same variable loops through all effects over and over again. # If this raises performance issues it has to be reconsidered # print("Check for same effect on numeric variable %s" % effect.fluent) conflict = False for other_effect in result: if isinstance(other_effect, pddl.FunctionAssignment): # print("comparing to %s -> Conflict? %s" % (other_effect.fluent, (effect.fluent == other_effect.fluent))) if (effect.fluent == other_effect.fluent): conflict = True break if conflict: print( "Warning, multiple effects on numeric variable %s, ignoring %s" % (other_effect.fluent, effect.fluent)) else: result.append(new_effect) else: assert isinstance(effect, pddl.Literal) contradiction = pddl.Effect(parameters, condition, effect.negate()) if not contradiction in result: result.append(new_effect) else: # We use add-after-delete semantics, keep positive effect if isinstance(contradiction.peffect, pddl.NegatedAtom): result.remove(contradiction) result.append(new_effect)