def instantiate_condition(action, is_static, args_from_predicate): parameters = {p.name for p in action.parameters} #if not parameters: # yield {} # return static_conditions = list( filter(is_static, get_literals(get_precondition(action)))) static_parameters = set( filter(is_parameter, flatten(atom.args for atom in static_conditions))) if not (parameters <= static_parameters): raise NotImplementedError( 'Could not instantiate action {} due to parameters: {}'.format( action.name, str_from_object(parameters - static_parameters))) atoms_from_cond = { condition: args_from_predicate[condition.predicate, get_constants(condition)] for condition in static_conditions } conditions, atoms = zip(*atoms_from_cond.items()) relations = [ Relation(conditions[index].args, atoms[index]) for index in compute_order(conditions, atoms) ] solution = solve_satisfaction(relations) for element in solution.body: yield solution.get_mapping(element)
def extract_axiom_plan(task, goals, negative_from_name, static_state=set()): import pddl_to_prolog import build_model import instantiate # TODO: only reinstantiate the negative axioms axioms_from_name = get_derived_predicates(task.axioms) derived_goals = {l for l in goals if l.predicate in axioms_from_name} axiom_from_action = get_necessary_axioms(derived_goals, task.axioms, negative_from_name) if not axiom_from_action: return [] conditions_from_predicate = defaultdict(set) for axiom, mapping in axiom_from_action.values(): for literal in get_literals(axiom.condition): conditions_from_predicate[literal.predicate].add(literal.rename_variables(mapping)) original_init = task.init original_actions = task.actions original_axioms = task.axioms # TODO: retrieve initial state based on if helpful task.init = {atom for atom in task.init if is_useful_atom(atom, conditions_from_predicate)} # TODO: store map from predicate to atom task.actions = axiom_from_action.keys() task.axioms = [] # TODO: maybe it would just be better to drop the negative throughout this process until this end with Verbose(False): model = build_model.compute_model(pddl_to_prolog.translate(task)) # Changes based on init task.actions = original_actions task.axioms = original_axioms opt_facts = instantiate.get_fluent_facts(task, model) | (task.init - static_state) mock_fluent = MockSet(lambda item: (item.predicate in negative_from_name) or (item in opt_facts)) instantiated_axioms = instantiate_necessary_axioms(model, static_state, mock_fluent, axiom_from_action) axiom_plan = extraction_helper(task.init, instantiated_axioms, derived_goals, negative_from_name) task.init = original_init return axiom_plan
def add_stream_costs(node_from_atom, instantiated, unit_efforts, effort_weight): # TODO: instantiate axioms with negative on effects for blocking # TODO: fluent streams using conditional effects. Special fluent predicate for inputs to constraint # This strategy will only work for relaxed to ensure that the current state is applied for instance in instantiated.actions: # TODO: prune stream actions here? # Ignores conditional effect costs facts = [] for precondition in get_literals(instance.action.precondition): if precondition.negated: continue args = [instance.var_mapping.get(arg, arg) for arg in precondition.args] literal = precondition.__class__(precondition.predicate, args) fact = fact_from_fd(literal) if fact in node_from_atom: facts.append(fact) #effort = COMBINE_OP([0] + [node_from_atom[fact].effort for fact in facts]) stream_plan = [] extract_stream_plan(node_from_atom, facts, stream_plan) if unit_efforts: effort = len(stream_plan) else: effort = scale_cost(sum([0] + [r.instance.get_effort() for r in stream_plan])) if effort_weight is not None: instance.cost += effort_weight*effort # TODO: bug! The FD instantiator prunes the result.external.stream_fact for result in stream_plan: # TODO: need to make multiple versions if several ways of achieving the action if is_optimizer_result(result): fact = substitute_expression(result.external.stream_fact, result.get_mapping()) atom = fd_from_fact(fact) instantiated.atoms.add(atom) effect = (tuple(), atom) instance.add_effects.append(effect)
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 extract_axiom_plan(task, action_instance, negative_from_name, static_state=set()): import pddl_to_prolog import build_model import axiom_rules import instantiate axioms_from_name = get_derived_predicates(task.axioms) derived_preconditions = {l for l in action_instance.precondition if l.predicate in axioms_from_name} nonderived_preconditions = {l for l in action_instance.precondition if l not in derived_preconditions} if not conditions_hold(task.init, nonderived_preconditions): return None axiom_from_action = get_necessary_axioms(action_instance, task.axioms, negative_from_name) if not axiom_from_action: return [] conditions_from_predicate = defaultdict(set) for axiom, mapping in axiom_from_action.values(): for literal in get_literals(axiom.condition): conditions_from_predicate[literal.predicate].add(literal.rename_variables(mapping)) original_init = task.init original_actions = task.actions original_axioms = task.axioms task.init = {atom for atom in task.init if is_useful_atom(atom, conditions_from_predicate)} # TODO: store map from predicate to atom task.actions = axiom_from_action.keys() task.axioms = [] # TODO: maybe it would just be better to drop the negative throughout this process until this end with Verbose(False): model = build_model.compute_model(pddl_to_prolog.translate(task)) # Changes based on init task.actions = original_actions task.axioms = original_axioms opt_facts = instantiate.get_fluent_facts(task, model) | (task.init - static_state) mock_fluent = MockSet(lambda item: (item.predicate in negative_from_name) or (item in opt_facts)) instantiated_axioms = instantiate_necessary_axioms(model, static_state, mock_fluent, axiom_from_action) goal_list = [] with Verbose(False): helpful_axioms, axiom_init, _ = axiom_rules.handle_axioms( [action_instance], instantiated_axioms, goal_list) axiom_init = set(axiom_init) axiom_effects = {axiom.effect for axiom in helpful_axioms} #assert len(axiom_effects) == len(axiom_init) for pre in list(derived_preconditions) + list(axiom_effects): if (pre not in axiom_init) and (pre.negate() not in axiom_init): axiom_init.add(pre.positive().negate()) axiom_from_atom = get_achieving_axioms(task.init | axiom_init, helpful_axioms, negative_from_name) axiom_plan = [] # Could always add all conditions success = extract_axioms(axiom_from_atom, derived_preconditions, axiom_plan, negative_from_name) task.init = original_init #if not success: # return None return axiom_plan
def get_instance_facts(instance, node_from_atom): # TODO: ignores conditional effect conditions facts = [] for precondition in get_literals(instance.action.precondition): if precondition.negated: continue args = apply_mapping(precondition.args, instance.var_mapping) literal = precondition.__class__(precondition.predicate, args) fact = fact_from_fd(literal) if fact in node_from_atom: facts.append(fact) return facts
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 instantiate_domain(task, prune_static=True): fluent_predicates = get_fluents(task) is_static = lambda a: isinstance(a, pddl.Atom) and (a.predicate not in fluent_predicates) fluent_facts = MockSet(lambda a: not prune_static or not is_static(a)) init_facts = set(task.init) function_assignments = get_function_assignments(task) type_to_objects = instantiate.get_objects_by_type(task.objects, task.types) constants_from_predicate = defaultdict(set) for action in task.actions + task.axioms: for atom in filter(is_static, get_literals(get_precondition(action))): constants = tuple((i, a) for i, a in enumerate(atom.args) if not is_parameter(a)) constants_from_predicate[atom.predicate].add(constants) predicate_to_atoms = defaultdict(set) args_from_predicate = defaultdict(set) for atom in filter(is_static, task.init): # TODO: compute which predicates might involve constants predicate_to_atoms[atom.predicate].add(atom) args_from_predicate[atom.predicate].add(atom.args) for constants in constants_from_predicate[atom.predicate]: if all(atom.args[i] == o for i, o in constants): args_from_predicate[atom.predicate, constants].add(atom.args) instantiated_actions = [] for action in task.actions: for variable_mapping in instantiate_condition(action, is_static, args_from_predicate): inst_action = action.instantiate(variable_mapping, init_facts, fluent_facts, type_to_objects, task.use_min_cost_metric, function_assignments, predicate_to_atoms) if inst_action: instantiated_actions.append(inst_action) instantiated_axioms = [] for axiom in task.axioms: for variable_mapping in instantiate_condition(axiom, is_static, args_from_predicate): inst_axiom = axiom.instantiate(variable_mapping, init_facts, fluent_facts) if inst_axiom: instantiated_axioms.append(inst_axiom) reachable_facts, reachable_operators = get_achieving_axioms(init_facts, instantiated_actions + instantiated_axioms) atoms = {atom for atom in (init_facts | set(reachable_facts)) if isinstance(atom, pddl.Atom)} relaxed_reachable = all(literal_holds(init_facts, goal) or goal in reachable_facts for goal in instantiate_goal(task.goal)) reachable_actions = [action for action in reachable_operators if isinstance(action, pddl.PropositionalAction)] reachable_axioms = [axiom for axiom in reachable_operators if isinstance(axiom, pddl.PropositionalAxiom)] return relaxed_reachable, atoms, reachable_actions, reachable_axioms
def compile_fluent_attachments(domain, externals): import pddl state_streams = set( filter(lambda e: isinstance(e, Stream) and e.is_fluent(), externals)) # is_special predicate_map = get_predicate_map(state_streams) if predicate_map and not os.path.exists(PYPLANNERS_PATH): raise NotImplementedError( 'Algorithm does not support fluent streams: {}'.format( [stream.name for stream in predicate_map.values()])) 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(effect) action.attachments = {} preconditions = [] for literal in get_conjunctive_parts(action.precondition): if not isinstance(literal, pddl.Literal): raise NotImplementedError(literal) if literal.predicate in predicate_map: stream = predicate_map[literal.predicate] if not stream.is_test(): raise NotImplementedError(stream) assert remap_certified(literal, stream) is not None action.attachments[literal] = stream else: preconditions.append(literal) 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 ]