def simple_from_durative_action(durative_actions, fluents): from pddlstream.algorithms.algorithm import get_predicates import pddl simple_actions = {} for action in durative_actions: parameters = convert_parameters(action.parameters) conditions = list(map(convert_condition, action.condition)) start_effects, end_effects = action.effects over_effects = [] effects = list( map(convert_effects, [start_effects, over_effects, end_effects])) static_condition = pddl.Conjunction( list({ part for condition in conditions for part in get_conjunctive_parts(condition.simplified()) if not isinstance(part, pddl.Truth) and not (get_predicates(part) & fluents) })) # TODO: deal with case where there are fluents actions = [] for i, (condition, effect) in enumerate(safe_zip(conditions, effects)): # TODO: extract the durations by pretending they are action costs actions.append( pddl.Action( SIMPLE_TEMPLATE.format(action.name, i), parameters, len(parameters), pddl.Conjunction([static_condition, condition]).simplified(), effect, None)) #actions[-1].dump() simple_actions[action] = actions return simple_actions
def fn(literal, action): if literal.predicate not in predicate_map: return literal # TODO: other checks on only inputs stream = predicate_map[literal.predicate] mapping = remap_certified(literal, stream) if mapping is None: # TODO: this excludes typing. This is not entirely safe return literal output_args = set(mapping[arg] for arg in stream.outputs) if isinstance(action, pddl.Action): # TODO: unified Action/Axiom effects for effect in action.effects: if isinstance(effect, pddl.Effect) and (output_args & set(effect.literal.args)): raise RuntimeError('Fluent stream outputs cannot be in action effects: {}'.format( effect.literal.predicate)) elif not stream.is_negated: axiom = action raise RuntimeError('Fluent stream outputs cannot be in an axiom: {}'.format(axiom.name)) blocked_args = safe_apply_mapping(stream.inputs, mapping) blocked_literal = literal.__class__(stream.blocked_predicate, blocked_args).negate() if stream.is_negated: conditions = [blocked_literal] conditions.extend(pddl.Atom(get_prefix(fact), safe_apply_mapping(get_args(fact), mapping)) # fd_from_fact for fact in stream.domain) # TODO: be careful when using imply return pddl.Conjunction(conditions) # TODO: prune redundant conditions return pddl.Conjunction([literal, blocked_literal])
def simple_from_durative_action(durative_actions, fluents): import pddl simple_actions = {} for action in durative_actions: parameters = convert_parameters(action.parameters) conditions = list(map(convert_condition, action.condition)) start_effects, end_effects = action.effects over_effects = [] effects = list( map(convert_effects, [start_effects, over_effects, end_effects])) static_condition = pddl.Conjunction( list({ literal for condition in conditions for literal in get_conjunctive_parts(condition.simplified()) if literal.predicate not in fluents })) actions = [] for i, (condition, effect) in enumerate(safe_zip(conditions, effects)): actions.append( pddl.Action( SIMPLE_TEMPLATE.format(action.name, i), parameters, len(parameters), pddl.Conjunction([static_condition, condition]).simplified(), effect, None)) #actions[-1].dump() simple_actions[action] = actions return simple_actions
def parse_action(alist, type_dict, predicate_dict): if DEBUG: print("parsing action %s" % [alist]) iterator = iter(alist) action_tag = next(iterator) assert action_tag == ":action", "Expected ':action' got '%s'" % action_tag name = next(iterator) parameters_tag_opt = next(iterator) if parameters_tag_opt == ":parameters": parameters = parse_typed_list(next(iterator), only_variables=True) precondition_tag_opt = next(iterator) else: parameters = [] precondition_tag_opt = parameters_tag_opt if precondition_tag_opt == ":precondition": precondition_list = next(iterator) if not precondition_list: # Note that :precondition () is allowed in PDDL. precondition = pddl.Conjunction([]) else: precondition = parse_condition(precondition_list, type_dict, predicate_dict) precondition = precondition.simplified() effect_tag = next(iterator) else: precondition = pddl.Conjunction([]) effect_tag = precondition_tag_opt assert effect_tag == ":effect" effect_list = next(iterator) eff = [] if effect_list: try: cost = parse_effects(effect_list, eff, type_dict, predicate_dict) if cost is None and eff: if DEBUG: print( "adding artificial effect (increase total-cost 1) to %s" % name) cost = pddl.NumericEffect( parse_assignment(['increase', 'total-cost', 1]) ) # artificially add a numeric effect that increases the total cost by one add_effect(cost, eff) assert (isinstance(cost, pddl.NumericEffect) ), "instance of cost is %s " % cost.__class__ except ValueError as e: raise SystemExit("Error in Action %s\nReason: %s." % (name, e)) for rest in iterator: assert False, rest if eff: return pddl.Action(name, parameters, len(parameters), precondition, eff, cost) else: return None
def parse_action(alist, type_dict, predicate_dict): iterator = iter(alist) action_tag = next(iterator) assert action_tag == ":action" name = next(iterator) parameters_tag_opt = next(iterator) if parameters_tag_opt == ":parameters": parameters = parse_typed_list(next(iterator), only_variables=True) precondition_tag_opt = next(iterator) else: parameters = [] precondition_tag_opt = parameters_tag_opt parameters_dict = dict((p.name, p) for p in parameters) if precondition_tag_opt == ":precondition": precondition_list = next(iterator) if not precondition_list: # Note that :precondition () is allowed in PDDL. precondition = pddl.Conjunction([]) else: precondition = parse_condition(precondition_list, type_dict, predicate_dict) precondition = precondition.simplified() effect_tag = next(iterator) else: precondition = pddl.Conjunction([]) effect_tag = precondition_tag_opt assert effect_tag == ":effect" effect_list = next(iterator) eff = [] if effect_list: try: cost = parse_effects(effect_list, eff, type_dict, predicate_dict) except ValueError as e: raise SystemExit("Error in Action %s\nReason: %s." % (name, e)) for rest in iterator: # Parse cost function if rest == ":cost": outer_op = None cost_list = next(iterator) cost = cost_node.CostNode("") cost = cost.parse_cost(cost_list, outer_op, predicate_dict, parameters_dict, cost) break for rest in iterator: assert False, rest if eff: return pddl.Action(name, parameters, len(parameters), precondition, eff, cost) else: return None
def instantiate_unsatisfiable(state, action, var_mapping, negative_from_name={}): precondition = [] for effect in action.effects: if effect.literal.predicate == UNSATISFIABLE: # Condition must be false for plan to succeed conditions = set(get_conjunctive_parts(effect.condition)) negative = { literal for literal in conditions if literal.predicate in negative_from_name } if not negative: continue assert len(negative) == 1 # TODO: handle the case where negative is not used (not (CFree ..)) normal_conjunction = pddl.Conjunction(conditions - negative) # TODO: assumes that can instantiate with just predicate_to_atoms normal_effect = pddl.Effect(effect.parameters, normal_conjunction, effect.literal) # TODO: avoid recomputing these objects_by_type = instantiate.get_objects_by_type([], []) predicate_to_atoms = instantiate.get_atoms_by_predicate(state) result = [] normal_effect.instantiate(var_mapping, state, {effect.literal}, objects_by_type, predicate_to_atoms, result) for _, _, _, mapping in result: for literal in negative: new_literal = literal.rename_variables(mapping).negate() assert (not new_literal.free_variables()) precondition.append(new_literal) return precondition
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 recurse(condition): existential_parts = [] other_parts = [] for part in condition.parts: part = recurse(part) if isinstance(part, pddl.ExistentialCondition): existential_parts.append(part) else: other_parts.append(part) if not existential_parts: return condition # Rule (1): Combine nested quantifiers. if isinstance(condition, pddl.ExistentialCondition): new_parameters = condition.parameters + existential_parts[ 0].parameters new_parts = existential_parts[0].parts return pddl.ExistentialCondition(new_parameters, new_parts) # Rule (2): Pull quantifiers out of conjunctions. assert isinstance(condition, pddl.Conjunction) new_parameters = [] new_conjunction_parts = other_parts for part in existential_parts: new_parameters += part.parameters new_conjunction_parts += part.parts new_conjunction = pddl.Conjunction(new_conjunction_parts) return pddl.ExistentialCondition(new_parameters, (new_conjunction, ))
def optimizer_conditional_effects(domain, externals): import pddl #from pddlstream.algorithms.scheduling.negative import get_negative_predicates # TODO: extend this to predicates if UNIVERSAL_TO_CONDITIONAL: negative_streams = list(filter(lambda e: e.is_negated, externals)) else: negative_streams = list( filter(lambda e: isinstance(e, ConstraintStream) and e.is_negated, externals)) negative_from_predicate = get_predicate_map(negative_streams) if not negative_from_predicate: return for action in domain.actions: universal_to_conditional(action) new_effects = [] for effect in action.effects: if effect.literal.predicate != UNSATISFIABLE: new_effects.append(effect) continue new_parts, stream_facts = process_conditional_effect( effect, negative_from_predicate) if not stream_facts: new_effects.append(effect) for stream_fact in stream_facts: new_effects.append( pddl.Effect(effect.parameters, pddl.Conjunction(new_parts), stream_fact)) action.effects = new_effects
def parse_online_goals(alist, type_dict, predicate_dict, subgoal): """Parse a PDDL condition when goals appear online. The condition is translated into NNF on the fly.""" online_goals = [] online_goals_availability = [] if subgoal: goals = alist else: goals = alist[1:] for goal in goals: tag = goal[0] args = goal[1:] if not tag == "and": if tag.isdigit(): online_goals_availability.append(tag) elif subgoal: return parse_literal(alist, type_dict, predicate_dict, negated=False) else: parsed = parse_literal(goal, type_dict, predicate_dict, negated=False) parsed.uniquify_variables({}) online_goals.append(parsed) else: inner_parts = [parse_online_goals(part, type_dict, predicate_dict, True) for part in args] conj = pddl.Conjunction(inner_parts) conj.uniquify_variables({}) online_goals.append(conj) return online_goals, online_goals_availability
def recurse(condition): if isinstance(condition, pddl.UniversalCondition): assert 1 == len(condition.parts) template = recurse(condition.parts[0]) type_lists = [] for param in condition.parameters: type_lists.append( filter(lambda obj: obj.type == param.type, task.objects)) object_combos = list(product(*type_lists)) types = [param.name for param in condition.parameters] copies = [] for combo in object_combos: copies.append( lit_replacement( template, dict(zip(types, [obj.name for obj in combo])))) return pddl.Conjunction(copies) else: new_parts = [recurse(part) for part in condition.parts] return condition.change_parts(new_parts)
def add_inequality_preconds(self, action, reachable_action_params): if reachable_action_params is None or len(action.parameters) < 2: return action inequal_params = [] combs = itertools.combinations(range(len(action.parameters)), 2) for pos1, pos2 in combs: for params in reachable_action_params[action]: if params[pos1] == params[pos2]: break else: inequal_params.append((pos1, pos2)) if inequal_params: precond_parts = [action.precondition] for pos1, pos2 in inequal_params: param1 = action.parameters[pos1].name param2 = action.parameters[pos2].name new_cond = pddl.NegatedAtom("=", (param1, param2)) precond_parts.append(new_cond) precond = pddl.Conjunction(precond_parts).simplified() return pddl.Action( action.name, action.parameters, action.num_external_parameters, precond, action.effects, action.cost) else: return action
def add_inequality_preconds(self, action, reachable_action_params): if reachable_action_params is None or len(action.parameters) < 2: return action new_cond_parts = [] combs = itertools.combinations(range(len(action.parameters)), 2) for pos1, pos2 in combs: for params in reachable_action_params[action.name]: if params[pos1] == params[pos2]: break else: param1 = pddl.Variable(action.parameters[pos1].name) param2 = pddl.Variable(action.parameters[pos2].name) new_cond = pddl.NegatedAtom("=", (param1, param2)) new_cond_parts.append(new_cond) if new_cond_parts: new_cond = list(action.condition) for time in (0, 2): # add inequalities to start and end condition cond_parts = list(action.condition[time].parts) if isinstance(action.condition[time], pddl.Literal): cond_parts = [action.condition[time]] cond_parts.extend(new_cond_parts) cond = pddl.Conjunction(cond_parts) new_cond[time] = cond return pddl.DurativeAction(action.name, action.parameters, action.grounding_call, action.duration, new_cond, action.effects) else: return action
def parse_action(alist, type_dict, predicate_dict): iterator = iter(alist) action_tag = next(iterator) assert action_tag == ":action" or action_tag == ':action' name = next(iterator) parameters_tag_opt = next(iterator) if parameters_tag_opt == ":parameters": parameters = parse_typed_list(next(iterator), only_variables=True) precondition_tag_opt = next(iterator) else: parameters = [] precondition_tag_opt = parameters_tag_opt if precondition_tag_opt == ":precondition": precondition_list = next(iterator) if not precondition_list: # Note that :precondition () is allowed in PDDL. precondition = pddl.Conjunction([]) else: precondition = parse_condition(precondition_list, type_dict, predicate_dict) precondition = precondition.simplified() effect_tag = next(iterator) else: precondition = pddl.Conjunction([]) effect_tag = precondition_tag_opt assert effect_tag == ":effect" effect_list = next(iterator) eff = [] if effect_list: try: cost = parse_effects(effect_list, eff, type_dict, predicate_dict) except ValueError as e: raise SystemExit("Error in Action %s\nReason: %s." % (name, e)) next(iterator) == ":duration" duration = next(iterator) if len(duration[2]) == 1: duration = pddl.NumericConstant(int(duration[2])) else: duration = pddl.f_expression.PrimitiveNumericExpression( duration[2][0], duration[2][1:]) for rest in iterator: assert False, rest if eff: return pddl.Action(name, parameters, len(parameters), precondition, eff, cost, duration) else: return None
def parse_action(alist, type_dict, predicate_dict): iterator = iter(alist) action_tag = next(iterator) assert action_tag == ":action" name = next(iterator) parameters_tag_opt = next(iterator) if parameters_tag_opt == ":parameters": parameters = parse_typed_list(next(iterator), only_variables=True) precondition_tag_opt = next(iterator) else: parameters = [] precondition_tag_opt = parameters_tag_opt if precondition_tag_opt == ":precondition": precondition_list = next(iterator) if not precondition_list: # Note that :precondition () is allowed in PDDL. precondition = pddl.Conjunction([]) else: precondition = parse_condition( precondition_list, type_dict, predicate_dict) precondition = precondition.simplified() effect_tag = next(iterator) else: precondition = pddl.Conjunction([]) effect_tag = precondition_tag_opt assert effect_tag == ":effect" effect_list = next(iterator) eff = [] if effect_list: try: cost = parse_effects( effect_list, eff, type_dict, predicate_dict) except ValueError as e: raise SystemExit("Error in Action %s\nReason: %s." % (name, e)) for rest in iterator: assert False, rest # if eff: # return pddl.Action(name, parameters, len(parameters), # precondition, eff, cost) # else: # return None return pddl.Action(name, parameters, len(parameters), precondition, eff, None)
def append_precondition(action, precondition): # action: pddl.Action if isinstance(action.precondition, pddl.Conjunction): action.precondition.parts += (copy.deepcopy(precondition), ) else: action.precondition = pddl.Conjunction( [action.precondition, copy.deepcopy(precondition)]) return
def add_unsatisfiable_to_goal(domain, goal_expression): import pddl from pddlstream.language.optimizer import UNSATISFIABLE add_predicate(domain, make_predicate(UNSATISFIABLE, [])) negated_atom = pddl.NegatedAtom(UNSATISFIABLE, tuple()) for action in domain.actions: if negated_atom not in action.precondition.parts: action.precondition = pddl.Conjunction( [action.precondition, negated_atom]).simplified() return And(goal_expression, Not((UNSATISFIABLE, )))
def add_unsatisfiable_to_goal(domain, goal_expression, negate_actions=False): import pddl add_predicate(domain, make_predicate(UNSATISFIABLE, [])) if negate_actions: negated_atom = pddl.NegatedAtom(UNSATISFIABLE, tuple()) for action in domain.actions: if negated_atom not in action.precondition.parts: action.precondition = pddl.Conjunction( [action.precondition, negated_atom]).simplified() return And(goal_expression, Not((UNSATISFIABLE, )))
def recurse(condition, used_variables): if isinstance(condition, pddl.Literal): typed_vars = [] conjunction_parts = [] new_args = [] for term in condition.args: typed, parts, new_term = term.compile_objectfunctions_aux( used_variables) typed_vars += typed conjunction_parts += parts new_args.append(new_term) if conjunction_parts == []: return condition else: new_literal = condition.__class__(condition.predicate, new_args) conjunction_parts.append(new_literal) conjunction = pddl.Conjunction(conjunction_parts) return pddl.ExistentialCondition(typed_vars, [conjunction]) elif isinstance(condition, pddl.FunctionComparison): typed_vars = [] conjunction_parts = [] new_parts = [] for part in condition.parts: typed, parts, new_part = part.compile_objectfunctions_aux( used_variables) typed_vars += typed conjunction_parts += parts new_parts.append(new_part) if conjunction_parts == []: return condition else: new_comparison = condition.__class__(condition.comparator, new_parts) conjunction_parts.append(new_comparison) conjunction = pddl.Conjunction(conjunction_parts) return pddl.ExistentialCondition(typed_vars, [conjunction]) else: new_parts = [ recurse(part, used_variables) for part in condition.parts ] return condition.change_parts(new_parts)
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 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 compile_fluents_as_attachments(domain, externals): import pddl state_streams = set( filter(lambda e: isinstance(e, Stream) and e.is_fluent(), externals)) # is_special if not state_streams: return externals predicate_map = get_predicate_map(state_streams) if predicate_map and (get_pyplanners_path() is None): # TODO: fluent streams with outputs # Could convert the free parameter to a constant raise NotImplementedError( 'Algorithm does not support fluent streams: {}'.format( [stream.name for stream in state_streams])) domain.constants.append(make_object(PLACEHOLDER.pddl)) for action in domain.actions: for effect in action.effects: # TODO: conditional effects if any(literal.predicate in predicate_map for literal in get_literals(effect.condition)): raise ValueError( 'Attachments cannot be in action effects: {}'.format( effect)) action.attachments = {} preconditions = set() for literal in get_conjunctive_parts(action.precondition): #if not isinstance(literal, pddl.Literal): # raise NotImplementedError('Only literals are supported: {}'.format(literal)) if not get_predicates(literal) & set(predicate_map): preconditions.add(literal) continue if not isinstance(literal, pddl.Literal): raise NotImplementedError(literal) # Drops the original precondition stream = predicate_map[literal.predicate] mapping = remap_certified(literal, stream) assert mapping is not None action.attachments[literal] = stream preconditions.update( pddl.Atom(EQ, (mapping[out], PLACEHOLDER.pddl)) for out in stream.outputs) preconditions.update( fd_from_fact(substitute_fact(fact, mapping)) for fact in stream.domain) action.precondition = pddl.Conjunction(preconditions).simplified() #fn = lambda l: pddl.Truth() if l.predicate in predicate_map else l #action.precondition = replace_literals(fn, action.precondition).simplified() #action.dump() return [ external for external in externals if external not in state_streams ]
def recurse(condition): disjunctive_parts = [] other_parts = [] for part in condition.parts: part = recurse(part) if isinstance(part, pddl.Disjunction): disjunctive_parts.append(part) else: other_parts.append(part) if not disjunctive_parts: return condition # Rule (1): Associativity of disjunction. if isinstance(condition, pddl.Disjunction): result_parts = other_parts for part in disjunctive_parts: result_parts.extend(part.parts) return pddl.Disjunction(result_parts) # Rule (2): Distributivity disjunction/existential quantification. if isinstance(condition, pddl.ExistentialCondition): parameters = condition.parameters result_parts = [ pddl.ExistentialCondition(parameters, (part, )) for part in disjunctive_parts[0].parts ] return pddl.Disjunction(result_parts) # Rule (3): Distributivity disjunction/conjunction. assert isinstance(condition, pddl.Conjunction) result_parts = [pddl.Conjunction(other_parts)] while disjunctive_parts: previous_result_parts = result_parts result_parts = [] parts_to_distribute = disjunctive_parts.pop().parts for part1 in previous_result_parts: for part2 in parts_to_distribute: result_parts.append(pddl.Conjunction((part1, part2))) return pddl.Disjunction(result_parts)
def universal_to_conditional(action): import pddl new_parts = [] unsatisfiable = fd_from_fact((UNSATISFIABLE,)) for quant in get_conjunctive_parts(action.precondition): if isinstance(quant, pddl.UniversalCondition): condition = quant.parts[0] # TODO: normalize first? if isinstance(condition, pddl.Disjunction) or isinstance(condition, pddl.Literal): action.effects.append(pddl.Effect(quant.parameters, condition.negate(), unsatisfiable)) continue new_parts.append(quant) action.precondition = pddl.Conjunction(new_parts)
def optimizer_conditional_effects(domain, externals): import pddl #from pddlstream.algorithms.scheduling.negative import get_negative_predicates # TODO: extend this to predicates if UNIVERSAL_TO_CONDITIONAL: negative_streams = list(filter(lambda e: e.is_negated(), externals)) else: negative_streams = list( filter( lambda e: isinstance(e, ConstraintStream) and e.is_negated(), externals)) negative_from_predicate = get_predicate_map(negative_streams) if not negative_from_predicate: return for action in domain.actions: universal_to_conditional(action) new_effects = [] for effect in action.effects: if effect.literal.predicate != UNSATISFIABLE: new_effects.append(effect) continue new_parts = [] stream_facts = [] for disjunctive in get_conjunctive_parts(effect.condition): for literal in get_disjunctive_parts(disjunctive): # TODO: assert only one disjunctive part if isinstance(literal, pddl.Literal) and ( literal.predicate in negative_from_predicate): stream = negative_from_predicate[literal.predicate] if not isinstance(stream, ConstraintStream): new_parts.append(literal) continue certified = find_unique( lambda f: get_prefix(f) == literal.predicate, stream.certified) mapping = get_mapping(get_args(certified), literal.args) stream_facts.append( fd_from_fact( substitute_expression(stream.stream_fact, mapping))) # TODO: add the negated literal as precondition here? else: new_parts.append(literal) if not stream_facts: new_effects.append(effect) for stream_fact in stream_facts: new_effects.append( pddl.Effect(effect.parameters, pddl.Conjunction(new_parts), stream_fact)) action.effects = new_effects
def parse_condition_aux(alist, negated, type_dict, predicate_dict): """Parse a PDDL condition. The condition is translated into NNF on the fly.""" # if DEBUG: print ("parsing condition aux %s" % [alist]) tag = alist[0] if is_function_comparison( alist ): # NFD conditions are always comparisons between 2 numeric expressions args = [parse_expression(arg) for arg in alist[1:]] assert len(args) == 2, args if negated: return pddl.NegatedFunctionComparison(tag, args, True) else: return pddl.FunctionComparison(tag, args, True) elif tag in ("and", "or", "not", "imply"): args = alist[1:] if tag == "imply": assert len(args) == 2 if tag == "not": assert len(args) == 1 return parse_condition_aux(args[0], not negated, type_dict, predicate_dict) elif tag in ("forall", "exists"): parameters = parse_typed_list(alist[1]) args = alist[2:] assert len(args) == 1 else: # if is_object_comparison(alist): # print("DEBUG: Object comparison!") # print(alist) return parse_literal(alist, type_dict, predicate_dict, negated=negated) if tag == "imply": parts = [ parse_condition_aux(args[0], not negated, type_dict, predicate_dict), parse_condition_aux(args[1], negated, type_dict, predicate_dict) ] tag = "or" else: parts = [ parse_condition_aux(part, negated, type_dict, predicate_dict) for part in args ] if tag == "and" and not negated or tag == "or" and negated: return pddl.Conjunction(parts) elif tag == "or" and not negated or tag == "and" and negated: return pddl.Disjunction(parts) elif tag == "forall" and not negated or tag == "exists" and negated: return pddl.UniversalCondition(parameters, parts) elif tag == "exists" and not negated or tag == "forall" and negated: return pddl.ExistentialCondition(parameters, parts)
def simplify_actions(opt_evaluations, action_plan, task, actions, unit_costs): # TODO: add ordering constraints to simplify the optimization import pddl import instantiate fluent_facts = MockSet() init_facts = set() type_to_objects = instantiate.get_objects_by_type(task.objects, task.types) results_from_head = get_results_from_head(opt_evaluations) action_from_name = {} function_plan = set() for i, (name, args) in enumerate(action_plan): action = find_unique(lambda a: a.name == name, actions) assert (len(action.parameters) == len(args)) # parameters = action.parameters[:action.num_external_parameters] var_mapping = {p.name: a for p, a in zip(action.parameters, args)} new_name = '{}-{}'.format(name, i) new_parameters = action.parameters[len(args):] new_preconditions = [] action.precondition.instantiate(var_mapping, init_facts, fluent_facts, new_preconditions) new_effects = [] for eff in action.effects: eff.instantiate(var_mapping, init_facts, fluent_facts, type_to_objects, new_effects) new_effects = [pddl.Effect([], pddl.Conjunction(conditions), effect) for conditions, effect in new_effects] cost = pddl.Increase(fluent=pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]), expression=pddl.NumericConstant(1)) # cost = None task.actions.append(pddl.Action(new_name, new_parameters, len(new_parameters), pddl.Conjunction(new_preconditions), new_effects, cost)) action_from_name[new_name] = (name, map(obj_from_pddl, args)) if not unit_costs: function_result = extract_function_results(results_from_head, action, args) if function_result is not None: function_plan.add(function_result) return action_from_name, list(function_plan)
def compile_to_exogenous_axioms(evaluations, domain, streams): import pddl 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) derived_map = {p: 'd-{}'.format(p) for p in certified_predicates} rename_derived = lambda a: rename_atom(a, derived_map) for action in domain.actions: action.precondition = replace_predicates(derived_map, action.precondition) for effect in action.effects: assert(isinstance(effect, pddl.Effect)) effect.condition = replace_predicates(derived_map, effect.condition) for axiom in domain.axioms: axiom.condition = replace_predicates(derived_map, axiom.condition) #fluent_predicates.update(certified_predicates) for stream in list(streams): if not isinstance(stream, Stream): raise NotImplementedError(stream) streams.append(create_static_stream(stream, evaluations, fluent_predicates, rename_future)) stream_atom = streams[-1].certified[0] domain.predicate_dict[get_prefix(stream_atom)] = pddl.Predicate(get_prefix(stream_atom), get_args(stream_atom)) precondition = pddl.Conjunction(tuple(map(fd_from_fact, (stream_atom,) + tuple(map(rename_derived, stream.domain))))) for fact in stream.certified: derived_fact = fd_from_fact(rename_derived(fact)) external_params = derived_fact.args internal_params = tuple(p for p in (stream.inputs + stream.outputs) if p not in derived_fact.args) parameters = tuple(pddl.TypedObject(p, 'object') for p in (external_params + internal_params)) #precondition = pddl.Conjunction(tuple(map(fd_from_fact, [stream_atom] + # list(map(rename_derived, stream.domain))))) #precondition = pddl.Disjunction([fd_from_fact(fact), precondition]) # TODO: quantifier domain.axioms.extend([ pddl.Axiom(name=derived_fact.predicate, parameters=parameters, num_external_parameters=len(external_params), condition=precondition), pddl.Axiom(name=derived_fact.predicate, parameters=parameters[:len(external_params)], num_external_parameters=len(external_params), condition=fd_from_fact(fact))]) stream.certified = tuple(set(stream.certified) | set(map(rename_future, stream.certified)))
def augment_goal(domain, goal_expression, negate_actions=False): # TODO: only do this if optimizers are present #return goal_expression import pddl predicate = pddl.predicates.Predicate(UNSATISFIABLE, tuple()) if predicate.name not in domain.predicate_dict: domain.predicates.append(predicate) domain.predicate_dict[predicate.name] = predicate if negate_actions: negated_atom = pddl.NegatedAtom(UNSATISFIABLE, tuple()) for action in domain.actions: if negated_atom not in action.precondition.parts: action.precondition = pddl.Conjunction( [action.precondition, negated_atom]).simplified() return And(goal_expression, Not((UNSATISFIABLE, )))
def convert_effects(effects): import pddl new_effects = make_effects([('_noop',)]) # To ensure the action has at least one effect for effect in effects: class_name = effect.__class__.__name__ if class_name == 'Effect': peffect_name = effect.peffect.__class__.__name__ if peffect_name in ('Increase', 'Decrease'): # TODO: currently ignoring numeric conditions continue new_effects.append(pddl.Effect(convert_parameters(effect.parameters), pddl.Conjunction(list(map(convert_condition, effect.condition))).simplified(), convert_condition(effect.peffect))) else: raise NotImplementedError(class_name) return new_effects