def variables_to_numbers(effect, conditions): new_effect_args = list(effect.args) rename_map = {} for i, arg in enumerate(effect.args): if arg[0] == "?": rename_map[arg] = i new_effect_args[i] = i new_effect = pddl.Atom(effect.predicate, new_effect_args) # There are three possibilities for arguments in conditions: # 1. They are variables that occur in the effect. In that case, # they are replaced by the corresponding position in the # effect, as indicated by the rename_map. # 2. They are constants. In that case, the unifier must guarantee # that they are matched appropriately. In that case, they are # not modified (remain strings denoting objects). # 3. They are variables that don't occur in the effect (are # projected away). This is only allowed in projection rules. # Such arguments are also not modified (remain "?x" strings). new_conditions = [] for cond in conditions: new_cond_args = [rename_map.get(arg, arg) for arg in cond.args] new_conditions.append(pddl.Atom(cond.predicate, new_cond_args)) return new_effect, new_conditions
def build_rules(self, rules): axiom = self.owner app_rule_head = get_axiom_predicate(axiom) app_rule_body = condition_to_rule_body(axiom.parameters, self.condition) rules.append((app_rule_body, app_rule_head)) params = axiom.parameters[:axiom.num_external_parameters] eff_rule_head = pddl.Atom(axiom.name, [par.name for par in params]) eff_rule_body = [app_rule_head] rules.append((eff_rule_body, eff_rule_head))
def substitute_complicated_goal(task): goal = task.goal if isinstance(goal, pddl.Literal): return elif isinstance(goal, pddl.Conjunction): for item in goal.parts: if not isinstance(item, pddl.Literal): break else: return new_axiom = task.add_axiom([], goal) task.goal = pddl.Atom(new_axiom.name, new_axiom.parameters)
def _rename_duplicate_variables(self, atom, new_conditions): used_variables = set() for i, var_name in enumerate(atom.args): if var_name[0] == "?": if var_name in used_variables: new_var_name = "%s@%d" % (var_name, len(new_conditions)) atom = atom.replace_argument(i, new_var_name) new_conditions.append( pddl.Atom("=", [var_name, new_var_name])) else: used_variables.add(var_name) return atom
def remove_free_effect_variables(self): """Remove free effect variables like the variable Y in the rule p(X, Y) :- q(X). This is done by introducing a new predicate @object, setting it true for all objects, and translating the above rule to p(X, Y) :- q(X), @object(Y). After calling this, no new objects should be introduced!""" # Note: This should never be necessary for typed domains. # Leaving it in at the moment regardless. must_add_predicate = False for rule in self.rules: eff_vars = get_variables([rule.effect]) cond_vars = get_variables(rule.conditions) if not eff_vars.issubset(cond_vars): must_add_predicate = True eff_vars -= cond_vars for var in sorted(eff_vars): rule.add_condition(pddl.Atom("@object", [var])) if must_add_predicate: print("Unbound effect variables: Adding @object predicate.") self.facts += [ Fact(pddl.Atom("@object", [obj])) for obj in self.objects ]
def parse_literal(alist, type_dict, predicate_dict, negated=False): if alist[0] == "not": assert len(alist) == 2 alist = alist[1] negated = not negated pred_id, arity = _get_predicate_id_and_arity( alist[0], type_dict, predicate_dict) if arity != len(alist) - 1: raise SystemExit("predicate used with wrong arity: (%s)" % " ".join(alist)) if negated: return pddl.NegatedAtom(pred_id, alist[1:]) else: return pddl.Atom(pred_id, alist[1:])
def convert_trivial_rules(self): """Convert rules with an empty condition into facts. This must be called after bounding rule effects, so that rules with an empty condition must necessarily have a variable-free effect. Variable-free effects are the only ones for which a distinction between ground and symbolic atoms is not necessary.""" must_delete_rules = [] for i, rule in enumerate(self.rules): if not rule.conditions: assert not get_variables([rule.effect]) self.add_fact( pddl.Atom(rule.effect.predicate, rule.effect.args)) must_delete_rules.append(i) if must_delete_rules: print("Trivial rules: Converted to facts.") for rule_no in must_delete_rules[::-1]: del self.rules[rule_no]
def parse_task(domain_pddl, task_pddl): domain_name, domain_requirements, types, type_dict, constants, predicates, predicate_dict, functions, actions, axioms \ = parse_domain_pddl(domain_pddl) task_name, task_domain_name, task_requirements, objects, init, goal, use_metric = parse_task_pddl(task_pddl, type_dict, predicate_dict) assert domain_name == task_domain_name requirements = pddl.Requirements(sorted(set( domain_requirements.requirements + task_requirements.requirements))) objects = constants + objects check_for_duplicates( [o.name for o in objects], errmsg="error: duplicate object %r", finalmsg="please check :constants and :objects definitions") init += [pddl.Atom("=", (obj.name, obj.name)) for obj in objects] return pddl.Task( domain_name, task_name, requirements, types, objects, predicates, functions, init, goal, actions, axioms, use_metric)
def condition_to_rule_body(parameters, condition): result = [] for par in parameters: result.append(par.get_atom()) if not isinstance(condition, pddl.Truth): if isinstance(condition, pddl.ExistentialCondition): for par in condition.parameters: result.append(par.get_atom()) condition = condition.parts[0] if isinstance(condition, pddl.Conjunction): parts = condition.parts else: parts = (condition, ) for part in parts: if isinstance(part, pddl.Falsity): # Use an atom in the body that is always false because # it is not initially true and doesn't occur in the # head of any rule. return [pddl.Atom("@always-false", [])] assert isinstance( part, pddl.Literal), "Condition not normalized: %r" % part if not part.negated: result.append(part) return result
def push(self, predicate, args): self.num_pushes += 1 eff_tuple = (predicate, ) + tuple(args) if eff_tuple not in self.enqueued: self.enqueued.add(eff_tuple) self.queue.append(pddl.Atom(predicate, list(args)))
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) assert goal[0] == ":goal" and len(goal) == 2 yield parse_condition(goal[1], type_dict, predicate_dict) use_metric = False for entry in iterator: if entry[0] == ":metric": if entry[1] == "minimize" and entry[2][0] == "total-cost": use_metric = True else: assert False, "Unknown metric." yield use_metric for entry in iterator: assert False, entry
def get_axiom_predicate(axiom): name = axiom variables = [par.name for par in axiom.parameters] if isinstance(axiom.condition, pddl.ExistentialCondition): variables += [par.name for par in axiom.condition.parameters] return pddl.Atom(name, variables)
def build_rules(self, rules): rule_head = pddl.Atom("@goal-reachable", []) rule_body = condition_to_rule_body([], self.condition) rules.append((rule_body, rule_head))
def add_rule(self, type, conditions, effect_vars): effect = pddl.Atom(next(self.name_generator), effect_vars) rule = pddl_to_prolog.Rule(conditions, effect) rule.type = type self.result.append(rule) return rule.effect
def project_rule(rule, conditions, name_generator): predicate = next(name_generator) effect_variables = set(rule.effect.args) & get_variables(conditions) effect = pddl.Atom(predicate, sorted(effect_variables)) projected_rule = Rule(conditions, effect) return projected_rule