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 add_either_rules(type, rules): if isinstance(type, tuple): assert type[0] == "either" for subtype in type[1:]: add_either_rules(subtype, rules) rule_head = pddl.Atom(type, [pddl.Variable("?x")]) rule_body = [pddl.Atom(subtype, [pddl.Variable("?x")])] rules.append((rule_body, rule_head))
def get_axiom_predicate(axiom): name = axiom variables = [pddl.Variable(par.name) for par in axiom.parameters] if isinstance(axiom.condition, pddl.ExistentialCondition): variables += [ pddl.Variable(par.name) for par in axiom.condition.parameters ] return pddl.Atom(name, variables)
def get_action_predicate(action): name = action variables = [pddl.Variable(par.name) for par in action.parameters] if isinstance(action.condition,list): for condition in action.condition: if isinstance(condition, pddl.ExistentialCondition): variables += [pddl.Variable(par.name) for par in condition.parameters] if isinstance(action.condition, pddl.ExistentialCondition): variables += [pddl.Variable(par.name) for par in action.condition.parameters] return pddl.Atom(name, variables)
def build_rules(self, rules, fluent_preds): axiom = self.owner app_rule_head = get_axiom_predicate(axiom) app_rule_body = list(condition_to_rule_body(axiom.parameters, self.condition)) rules.append((app_rule_body, app_rule_head)) eff_rule_head = pddl.Atom(axiom.name, [pddl.Variable(par.name) for par in axiom.parameters]) eff_rule_body = [app_rule_head] rules.append((eff_rule_body, eff_rule_head))
def build_rules(self, rules, fluent_preds, modules): effect = self.owner rule_head = effect.peffect # FIXME ModuleCalls do not have any effect (for now) # possibly add the affected fluents here? fluent_head = None module_heads = [] if not isinstance(rule_head, pddl.NegatedAtom): if isinstance(rule_head, pddl.ModuleCall): # Determine the fluents this module sets my_module = None for module in modules: if module.name == rule_head.name: my_module = module break if my_module: # we need to rename all args, s.th. the module effect's # fluents args are the same as in the modulecall. assert len(my_module.parameters) == len(rule_head.args) renamings = {} for param, arg in zip(my_module.parameters, rule_head.args): pVar = pddl.Variable(param.name) renamings[pVar] = arg # and add a function and fluent predicate for each for fluent in my_module.effects: new_fluent = fluent.rename_variables(renamings) module_heads.append(get_function_predicate(new_fluent)) module_heads.append( get_fluent_function_predicate(new_fluent)) # finally disable the original single head. rule_head = None elif isinstance(rule_head, pddl.FunctionAssignment): fluent = rule_head.fluent rule_head = get_function_predicate(fluent) fluent_head = get_fluent_function_predicate(fluent) rule_body = [get_action_predicate(self.action)] if self.effecttime != None: # we use the start condition in any case rule_body += condition_to_rule_body([], self.condition[0]) # for end effects we use all conditions if self.effecttime == 1: rule_body += condition_to_rule_body([], self.condition[1]) rule_body += condition_to_rule_body([], self.condition[2]) else: rule_body += condition_to_rule_body([], self.condition) if rule_head: rules.append((rule_body, rule_head)) if fluent_head: rules.append((rule_body, fluent_head)) for head in module_heads: rules.append((rule_body, head))
def condition_to_rule_body(parameters, condition, fluent_preds = None): for par in parameters: yield pddl.Atom(par.type, [pddl.Variable(par.name)]) if not isinstance(condition, pddl.Truth): if isinstance(condition, pddl.ExistentialCondition): for par in condition.parameters: yield pddl.Atom(par.type, [pddl.Variable(par.name)]) condition = condition.parts[0] if isinstance(condition, pddl.Conjunction): parts = condition.parts else: parts = (condition,) for part in parts: assert isinstance(part, pddl.Literal) or isinstance(part,pddl.FunctionComparison), "Condition not normalized" if isinstance(part, pddl.Literal): if not part.negated: if fluent_preds == None or part.predicate not in fluent_preds: yield part elif fluent_preds == None: # part is FunctionComparison primitives = part.primitive_numeric_expressions() for pne in primitives: yield get_function_predicate(pne)
def _rename_duplicate_variables(self, atom, new_conditions): used_variables = set() new_args = list(atom.args) for i, var in enumerate(atom.args): if isinstance(var, pddl.Variable): if var in used_variables: new_var_name = "%s@%d" % (var.name, len(new_conditions)) new_var = pddl.Variable(new_var_name) new_args[i] = new_var new_conditions.append(pddl.Atom("=", [var, new_var])) else: used_variables.add(var) atom.args = tuple(new_args)
def condition_to_rule_body(parameters, condition, fluent_preds=None): for par in parameters: yield pddl.Atom(par.type, [pddl.Variable(par.name)]) if not isinstance(condition, pddl.Truth): if isinstance(condition, pddl.ExistentialCondition): for par in condition.parameters: yield pddl.Atom(par.type, [pddl.Variable(par.name)]) condition = condition.parts[0] if isinstance(condition, pddl.Conjunction): parts = condition.parts else: parts = (condition, ) for part in parts: # FIXME Modulecall might be at lowest level assert isinstance(part, pddl.Literal) or isinstance( part, pddl.FunctionComparison) or isinstance( part, pddl.ModuleCall), "Condition not normalized" if isinstance(part, pddl.Literal): if not part.negated: if fluent_preds == None or part.predicate not in fluent_preds: yield part elif isinstance(part, pddl.ModuleCall): # FIXME: yield nothing for that? # FIXME: this might be wrong - need to check, if rules are used # to build the task, then we would remove this wrongly # BUT this should be equivalent to TRUTH in the rule body # Alternativ: Make one of these "Atoms" (being true and holding module as "owner") # howevery they are used... # I guess we might wanna yield a truth "ModuleAtom" here... # or CAN WE IGNORE THIS? # there are also only non-negated literals in here... pass elif fluent_preds == None: # part is FunctionComparison primitives = part.primitive_numeric_expressions() for pne in primitives: yield get_function_predicate(pne)
def find_unique_variables(action, invariant): # find unique names for invariant variables params = set([p.name for p in action.parameters]) for eff in action.effects[0]: params.update([p.name for p in eff.parameters]) for eff in action.effects[1]: params.update([p.name for p in eff.parameters]) inv_vars = [] counter = itertools.count() for _ in xrange(invariant.arity()): while True: new_name = "?v%i" % counter.next() if new_name not in params: inv_vars.append(pddl.Variable(new_name)) break return inv_vars
def remove_object_functions_from_durations(task): for act in task.durative_actions: used_variables = [var.name for var in act.parameters] for time in range(2): for index, (op, exp) in enumerate(act.duration[time]): typed_vars, function_terms, new_term = \ exp.compile_objectfunctions_aux(used_variables, recurse_object_terms=False) act.duration[time][index] = (op, new_term) act.parameters += typed_vars new_conditions = [] assert len(typed_vars) == len(function_terms) new_conditions = [act.condition[time]] for var, term in zip(typed_vars, function_terms): variable = pddl.Variable(var.name) new_condition = pddl.Atom("=", [variable, term]) new_conditions.append(new_condition) act.condition[time] = pddl.Conjunction(new_conditions)
def instantiate(task, model): relaxed_reachable = False fluent_facts = get_fluent_facts(task, model) fluent_functions = get_fluent_functions(model) ## HACK: This is a not very clean way of initializing the previously ## added functions that store the duration of an action to a haphazardly value for atom in model: if isinstance(atom.predicate,str) and atom.predicate.startswith("defined!duration_"): pne = pddl.PrimitiveNumericExpression(atom.predicate.replace("defined!","",1),atom.args) value = pddl.NumericConstant(1.0) init_assign = pddl.Assign(pne, value) task.init.append(init_assign) init_facts = set(task.init) # TODO adapt init_function_vals = init_function_values(init_facts) # print "** fluent functions" # for function in fluent_functions: # function.dump() # print "** fluent facts" # for fact in fluent_facts: # print fact # print "** init facts" # for fact in init_facts: # print fact type_to_objects = get_objects_by_type(task.objects,task.types) instantiated_actions = [] instantiated_durative_actions = [] instantiated_axioms = [] instantiated_numeric_axioms = set() new_constant_numeric_axioms = set() reachable_action_parameters = defaultdict(list) for atom in model: if isinstance(atom.predicate, pddl.Action): action = atom.predicate parameters = action.parameters if isinstance(action.condition, pddl.ExistentialCondition): parameters = list(parameters) parameters += action.condition.parameters variable_mapping = dict([(pddl.Variable(par.name), arg) for par, arg in zip(parameters, atom.args)]) inst_action = action.instantiate(variable_mapping, init_facts, fluent_facts, init_function_vals, fluent_functions, task, new_constant_numeric_axioms, type_to_objects) if inst_action: instantiated_actions.append(inst_action) elif isinstance(atom.predicate, pddl.DurativeAction): action = atom.predicate parameters = action.parameters reachable_action_parameters[action.name].append(parameters) for condition in action.condition: if isinstance(condition,pddl.ExistentialCondition): parameters = list(parameters) parameters += condition.parameters variable_mapping = dict([(pddl.Variable(par.name), arg) for par, arg in zip(parameters, atom.args)]) inst_action = action.instantiate(variable_mapping, init_facts, fluent_facts, init_function_vals, fluent_functions, task, new_constant_numeric_axioms, type_to_objects) if inst_action: instantiated_durative_actions.append(inst_action) elif isinstance(atom.predicate, pddl.Axiom): axiom = atom.predicate parameters = axiom.parameters if isinstance(axiom.condition, pddl.ExistentialCondition): parameters = list(parameters) parameters += axiom.condition.parameters variable_mapping = dict([(pddl.Variable(par.name), arg) for par, arg in zip(parameters, atom.args)]) inst_axiom = axiom.instantiate(variable_mapping, init_facts, fluent_facts, fluent_functions, init_function_vals, task, new_constant_numeric_axioms) if inst_axiom: instantiated_axioms.append(inst_axiom) elif isinstance(atom.predicate, pddl.NumericAxiom): axiom = atom.predicate variable_mapping = dict([(pddl.Variable(par.name), arg) for par, arg in zip(axiom.parameters, atom.args)]) new_constant_numeric_axioms = set() inst_axiom = axiom.instantiate(variable_mapping, fluent_functions, init_function_vals, task, new_constant_numeric_axioms) instantiated_numeric_axioms.add(inst_axiom) elif atom.predicate == "@goal-reachable": relaxed_reachable = True instantiated_numeric_axioms |= new_constant_numeric_axioms return (relaxed_reachable, fluent_facts, fluent_functions, instantiated_actions, instantiated_durative_actions, instantiated_axioms, instantiated_numeric_axioms, reachable_action_parameters)
def instantiate(task, model): relaxed_reachable = False fluent_facts = get_fluent_facts(task, model) fluent_functions = get_fluent_functions(model) ## HACK: This is a not very clean way of initializing the previously ## added functions that store the duration of an action to a haphazardly value for atom in model: if isinstance(atom.predicate, str) and atom.predicate.startswith("defined!duration_"): pne = pddl.PrimitiveNumericExpression( atom.predicate.replace("defined!", "", 1), atom.args) value = pddl.NumericConstant(1.0) init_assign = pddl.Assign(pne, value) task.init.append(init_assign) init_facts = set(task.init) # TODO adapt init_function_vals = init_function_values(init_facts) # Determine initial facts, that are not fluents => constant facts, that a module might need init_constant_fluents = set(init_function_vals) init_constant_fluents.difference_update( fluent_functions ) # all fluents that are in init, but are NOT a fluent -> constant # Now get the assigned values from the init_facts for the constant fluents init_constant_numeric_facts = set( ) # This will hold Assigns that assign the fluents for i in init_constant_fluents: for j in init_facts: if isinstance(j, pddl.Assign): if isinstance(j.fluent, pddl.PrimitiveNumericExpression): if j.fluent is i: # Assign in init_fact assign this (i) fluent init_constant_numeric_facts.add(j) # Now get predicates that are in init, but are not fluent_facts init_constant_predicate_facts = set() for i in init_facts: if isinstance(i, pddl.Atom): # do NOT consider PNEs, etc. if i not in fluent_facts: # only consider non-fluents if i.predicate is not "=": # hack to remove the intermediate '=' fluents init_constant_predicate_facts.add(i) # print "** fluent functions" # for function in fluent_functions: # function.dump() # print "** fluent facts" # for fact in fluent_facts: # print fact # print "** init facts" # for fact in init_facts: # print fact type_to_objects = get_objects_by_type(task.objects, task.types) instantiated_actions = [] instantiated_durative_actions = [] instantiated_axioms = [] instantiated_numeric_axioms = set() new_constant_numeric_axioms = set() reachable_action_parameters = defaultdict(list) instantiated_modules = set() for atom in model: if isinstance(atom.predicate, pddl.Action): action = atom.predicate parameters = action.parameters if isinstance(action.condition, pddl.ExistentialCondition): parameters = list(parameters) parameters += action.condition.parameters variable_mapping = dict([ (pddl.Variable(par.name), arg) for par, arg in zip(parameters, atom.args) ]) inst_action = action.instantiate(variable_mapping, init_facts, fluent_facts, init_function_vals, fluent_functions, task, new_constant_numeric_axioms, instantiated_modules, type_to_objects) if inst_action: instantiated_actions.append(inst_action) elif isinstance(atom.predicate, pddl.DurativeAction): action = atom.predicate parameters = action.parameters reachable_action_parameters[action.name].append(parameters) for condition in action.condition: if isinstance(condition, pddl.ExistentialCondition): parameters = list(parameters) parameters += condition.parameters variable_mapping = dict([ (pddl.Variable(par.name), arg) for par, arg in zip(parameters, atom.args) ]) inst_action = action.instantiate(variable_mapping, init_facts, fluent_facts, init_function_vals, fluent_functions, task, new_constant_numeric_axioms, instantiated_modules, type_to_objects) if inst_action: instantiated_durative_actions.append(inst_action) elif isinstance(atom.predicate, pddl.Axiom): axiom = atom.predicate parameters = axiom.parameters if isinstance(axiom.condition, pddl.ExistentialCondition): parameters = list(parameters) parameters += axiom.condition.parameters variable_mapping = dict([ (pddl.Variable(par.name), arg) for par, arg in zip(parameters, atom.args) ]) inst_axiom = axiom.instantiate(variable_mapping, init_facts, fluent_facts, fluent_functions, init_function_vals, task, new_constant_numeric_axioms, instantiated_modules) if inst_axiom: instantiated_axioms.append(inst_axiom) elif isinstance(atom.predicate, pddl.NumericAxiom): axiom = atom.predicate variable_mapping = dict([ (pddl.Variable(par.name), arg) for par, arg in zip(axiom.parameters, atom.args) ]) new_constant_numeric_axioms = set() inst_axiom = axiom.instantiate(variable_mapping, fluent_functions, init_function_vals, task, new_constant_numeric_axioms) instantiated_numeric_axioms.add(inst_axiom) elif atom.predicate == "@goal-reachable": relaxed_reachable = True instantiated_numeric_axioms |= new_constant_numeric_axioms return (relaxed_reachable, fluent_facts, fluent_functions, instantiated_actions, instantiated_durative_actions, instantiated_axioms, instantiated_numeric_axioms, instantiated_modules, init_constant_predicate_facts, init_constant_numeric_facts, reachable_action_parameters)