def get_fluents(init, action_instances): fluents = set() for action in action_instances: # TODO: just actions if no action_instances for cond, eff in action.add_effects: assert not cond if not literal_holds(init, eff): fluents.add(eff) for cond, eff in action.del_effects: assert not cond if not literal_holds(init, eff.negate()): fluents.add(eff) return fluents
def recover_axioms_plans2(instantiated, action_instances): #import axiom_rules #with Verbose(False): # normalized_axioms, axiom_init, axiom_layer_dict = axiom_rules.handle_axioms( # [], instantiated.axioms, instantiated.goal_list) #state = set(instantiated.task.init + axiom_init) normalized_axioms = instantiated.axioms # TODO: ignoring negated because cannot reinstantiate correctly state = set(instantiated.task.init) fluents = get_fluents(state, action_instances) unprocessed_from_atom = defaultdict(list) fluents_from_axiom = {} remaining_from_axiom = {} for axiom in normalized_axioms: fluent_conditions = [] for literal in axiom.condition: if literal.positive() in fluents: fluent_conditions.append(literal) elif not literal_holds(state, literal): fluent_conditions = None break if fluent_conditions is None: continue for literal in fluent_conditions: unprocessed_from_atom[literal].append(axiom) fluents_from_axiom[id(axiom)] = len(fluent_conditions) remaining_from_axiom[id(axiom)] = fluents_from_axiom[id(axiom)] static_axioms = [ axiom for axiom, num in fluents_from_axiom.items() if num == 0 ] axiom_plans = [] for action in action_instances + [ get_goal_instance(instantiated.task.goal) ]: axiom_from_atom = mark_iteration(state, unprocessed_from_atom, fluents_from_axiom, remaining_from_axiom, static_axioms) preimage = [] for literal in action.precondition: if not literal_holds(state, literal): preimage.append(literal) assert literal in axiom_from_atom for cond, eff in (action.add_effects + action.del_effects): # TODO: add conditional effects that must hold here assert not cond axiom_plans.append([]) assert extract_axioms(axiom_from_atom, preimage, axiom_plans[-1]) apply_action(state, action) return axiom_plans
def __init__(self, instantiated, action_instances=[]): derived_predicates = get_derived_predicates(instantiated.task.axioms) conditions = { literal.positive() for axiom in instantiated.axioms for literal in axiom.condition } state = set(instantiated.task.init) fluents = get_fluents(state, action_instances) & conditions self.fluent_order = list(fluents) applicable_axioms = [] axiom_from_literal = defaultdict(list) # TODO: could also just use get_achieving_axioms self.root = SuccessorNode() for axiom in instantiated.axioms: if all((l.predicate in derived_predicates) or ( l.positive() in fluents) or literal_holds(state, l) for l in axiom.condition): applicable_axioms.append(axiom) for literal in axiom.condition: if literal in fluents: axiom_from_literal[literal].append(axiom) fluent_conds = { l.positive(): not l.negated for l in axiom.condition } node = self.root for atom in self.fluent_order: value = fluent_conds.get(atom, None) node = node.get_child(value) node.instances.append(axiom)
def recover_axioms_plans(instantiated, action_instances): #axioms, axiom_init, _ = axiom_rules.handle_axioms( # instantiated.actions, instantiated.axioms, instantiated.goal_list) new_action_instances = [ copy.deepcopy(instance) for instance in action_instances ] axioms, axiom_init = instantiated.axioms, [ ] # TODO: bug when needing to reachieve negated axioms_from_effect = defaultdict(list) for axiom in axioms: axioms_from_effect[axiom.effect].append(axiom) axioms_from_name = get_derived_predicates(instantiated.task.axioms) state = set(instantiated.task.init) | set(axiom_init) axiom_plans = [] for action in new_action_instances + [ get_goal_instance(instantiated.task.goal) ]: all_conditions = list(get_precondition(action)) + list( flatten(cond for cond, _ in action.add_effects + action.del_effects)) axioms = backtrack_axioms(all_conditions, axioms_from_effect, set()) axiom_from_atom, _ = get_achieving_axioms(state, axioms) action.applied_effects = [] for effects in [action.add_effects, action.del_effects]: negate = (effects is action.del_effects) for i, (conditions, effect) in reversed(list(enumerate(effects))): if all( literal_holds(state, literal) or ( literal in axiom_from_atom) for literal in conditions): action.precondition.extend(conditions) effects[i] = ([], effect) action.applied_effects.append( effect.negate() if negate else effect) else: effects.pop(i) # RuntimeError: Preimage fact ('new-axiom@0',) is not achievable! #precondition = action.precondition # TODO: strange bug if this applies precondition = [ literal for literal in action.precondition if literal.predicate in axioms_from_name ] axiom_plans.append([]) success = extract_axioms(state, axiom_from_atom, precondition, axiom_plans[-1]) if not success: print(all_conditions) print(action) print(axioms) raise RuntimeError('Could not extract axioms') apply_action(state, action) return new_action_instances, axiom_plans
def get_successors(self, atom_order, state): if len(atom_order) <= self.depth: return self.instances atom = atom_order[self.depth] instances = [] for value, node in self.children.items(): if (value is None) or (literal_holds(state, atom) is value): instances.extend(node.get_successors(atom_order, state)) return instances
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 if not negative_from_name: return [] axioms_from_name = get_derived_predicates(task.axioms) derived_goals = {l for l in goals if l.predicate in axioms_from_name} assert all( literal_holds(task.init, l) # or (l.predicate in negative_from_name) for l in set(goals) - derived_goals) 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(verbose=False): model = build_model.compute_model( pddl_to_prolog.translate(task)) # Changes based on init 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 task.actions = original_actions task.axioms = original_axioms return axiom_plan
def extract_axioms(state, axiom_from_atom, conditions, axiom_plan, negated_from_name={}): success = True for fact in filter_negated(conditions, negated_from_name): if literal_holds(state, fact): continue if fact not in axiom_from_atom: print('Fact is not achievable:', fact) success = False continue axiom = axiom_from_atom[fact] if (axiom is None) or (axiom in axiom_plan): continue extract_axioms(state, axiom_from_atom, axiom.condition, axiom_plan, negated_from_name=negated_from_name) axiom_plan.append(axiom) return success
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 recover_axioms_plans(instantiated, action_instances): task = instantiated.task derived_predicates = get_derived_predicates(task.axioms) state = set(task.init) axiom_plans = [] for action_instance in action_instances + [get_goal_instance(task.goal)]: # TODO: apply all axiom_instances unaffected by negative conditions preimage = list(plan_preimage([action_instance], [])) axiom_instances = filter( lambda ax: all(l.predicate in derived_predicates or literal_holds( state, l) for l in ax.condition), instantiated.axioms) # Only instantiate if preimage has goal axiom_plan = extraction_helper(state, axiom_instances, preimage) assert axiom_plan is not None axiom_plans.append(axiom_plan) apply_action(state, action_instance) return axiom_plans
def mark_iteration(state, axioms_from_literal, fluents_from_axiom, remaining_from_axiom, static_axioms): axioms_from_atom = defaultdict(list) for literal in axioms_from_literal: if literal_holds(state, literal): axioms_from_atom[literal].append(None) queue = deque(axioms_from_atom.keys()) for axiom in static_axioms: mark_axiom(queue, remaining_from_axiom, axiom, axioms_from_atom) while queue: literal = queue.popleft() for axiom in axioms_from_literal[literal]: remaining_from_axiom[id(axiom)] -= 1 mark_axiom(queue, remaining_from_axiom, axiom, axioms_from_atom) for literal, axioms in axioms_from_atom.items(): for axiom in axioms: if axiom is not None: remaining_from_axiom[id(axiom)] = fluents_from_axiom[id(axiom)] # TODO: still some overhead here # TODO: could process these layer by layer instead return {atom: axioms[0] for atom, axioms in axioms_from_atom.items()}
def get_achieving_axioms(state, operators, negated_from_name={}): # TODO: order by stream effort # marking algorithm for propositional Horn logic unprocessed_from_literal = defaultdict(list) operator_from_literal = {} remaining_from_stream = {} reachable_operators = set() # TODO: only keep facts queue = deque() def process_axiom(op, effect): reachable_operators.add(id(op)) if effect not in operator_from_literal: operator_from_literal[effect] = op queue.append(effect) # TODO: could produce a list of all derived conditions for op in operators: preconditions = get_precondition(op) for cond, effect in get_conditional_effects(op): conditions = cond + preconditions remaining_from_stream[id(op), effect] = 0 for literal in filter_negated(conditions, negated_from_name): if literal_holds(state, literal): operator_from_literal[literal] = None else: remaining_from_stream[id(op), effect] += 1 unprocessed_from_literal[literal].append((op, effect)) if remaining_from_stream[id(op), effect] == 0: process_axiom(op, effect) while queue: literal = queue.popleft() for op, effect in unprocessed_from_literal[literal]: remaining_from_stream[id(op), effect] -= 1 if remaining_from_stream[id(op), effect] == 0: process_axiom(op, effect) return operator_from_literal, [ op for op in operators if id(op) in reachable_operators ]
def recover_axioms_plans(instantiated, action_instances): #axioms, axiom_init, _ = axiom_rules.handle_axioms( # instantiated.actions, instantiated.axioms, instantiated.goal_list) axioms, axiom_init = instantiated.axioms, [ ] # TODO: bug when needing to reachieve negated axioms_from_effect = defaultdict(list) for axiom in axioms: axioms_from_effect[axiom.effect].append(axiom) state = set(instantiated.task.init) | set(axiom_init) axiom_plans = [] for action in action_instances + [ get_goal_instance(instantiated.task.goal) ]: all_conditions = list(get_precondition(action)) + list( flatten(cond for cond, _ in action.add_effects + action.del_effects)) axioms = backtrack_axioms(all_conditions, axioms_from_effect, set()) axiom_from_atom, _ = get_achieving_axioms(state, axioms) action.applied_effects = [] for effects in [action.add_effects, action.del_effects]: negate = effects is action.del_effects for i, (conditions, effect) in reversed(list(enumerate(effects))): if all( literal_holds(state, literal) or ( literal in axiom_from_atom) for literal in conditions): action.precondition.extend(conditions) effects[i] = ([], effect) action.applied_effects.append( effect.negate() if negate else effect) else: effects.pop(i) axiom_plans.append([]) assert extract_axioms(state, axiom_from_atom, action.precondition, axiom_plans[-1]) apply_action(state, action) return axiom_plans