00001
00002
00003
00004 import itertools
00005
00006 import normalize
00007 import pddl
00008
00009 class PrologProgram:
00010 def __init__(self):
00011 self.facts = []
00012 self.rules = []
00013 self.objects = set()
00014 def predicate_name_generator():
00015 for count in itertools.count():
00016 yield "p$%d" % count
00017 self.new_name = predicate_name_generator()
00018 def add_fact(self, atom):
00019 self.facts.append(Fact(atom))
00020 self.objects |= set(atom.args)
00021 def add_rule(self, rule):
00022 self.rules.append(rule)
00023 def dump(self):
00024 for fact in self.facts:
00025 print fact
00026 for rule in self.rules:
00027 print getattr(rule, "type", "none"), rule
00028 def normalize(self):
00029
00030
00031
00032
00033
00034 self.remove_free_effect_variables()
00035 self.split_duplicate_arguments()
00036 self.convert_trivial_rules()
00037 def split_rules(self):
00038 import split_rules
00039
00040
00041
00042
00043 new_rules = []
00044 for rule in self.rules:
00045 new_rules += split_rules.split_rule(rule, self.new_name)
00046 self.rules = new_rules
00047 def remove_free_effect_variables(self):
00048 """Remove free effect variables like the variable Y in the rule
00049 p(X, Y) :- q(X). This is done by introducing a new predicate
00050 @object, setting it true for all objects, and translating the above
00051 rule to p(X, Y) :- q(X), @object(Y).
00052 After calling this, no new objects should be introduced!"""
00053
00054
00055
00056 must_add_predicate = False
00057 for rule in self.rules:
00058 eff_vars = get_variables([rule.effect])
00059 cond_vars = get_variables(rule.conditions)
00060 if not eff_vars.issubset(cond_vars):
00061 must_add_predicate = True
00062 eff_vars -= cond_vars
00063 for var in eff_vars:
00064 rule.add_condition(pddl.Atom("@object", [var]))
00065 if must_add_predicate:
00066 print "Unbound effect variables: Adding @object predicate."
00067 self.facts += [Fact(pddl.Atom("@object", [obj])) for obj in self.objects]
00068 def split_duplicate_arguments(self):
00069 """Make sure that no variable occurs twice within the same symbolic fact,
00070 like the variable X does in p(X, Y, X). This is done by renaming the second
00071 and following occurrences of the variable and adding equality conditions.
00072 For example p(X, Y, X) is translated to p(X, Y, X@0) with the additional
00073 condition =(X, X@0); the equality predicate must be appropriately instantiated
00074 somewhere else."""
00075 printed_message = False
00076 for rule in self.rules:
00077 if rule.rename_duplicate_variables() and not printed_message:
00078 print "Duplicate arguments: Adding equality conditions."
00079 printed_message = True
00080 def convert_trivial_rules(self):
00081 """Convert rules with an empty condition into facts.
00082 This must be called after bounding rule effects, so that rules with an
00083 empty condition must necessarily have a variable-free effect.
00084 Variable-free effects are the only ones for which a distinction between
00085 ground and symbolic atoms is not necessary."""
00086 must_delete_rules = []
00087 for i, rule in enumerate(self.rules):
00088 if not rule.conditions:
00089 assert not get_variables([rule.effect])
00090 self.add_fact(pddl.Atom(rule.effect.predicate, rule.effect.args))
00091 must_delete_rules.append(i)
00092 if must_delete_rules:
00093 print "Trivial rules: Converted to facts."
00094 for rule_no in must_delete_rules[::-1]:
00095 del self.rules[rule_no]
00096
00097 def get_variables(symbolic_atoms):
00098 variables = set()
00099 for sym_atom in symbolic_atoms:
00100
00101 variables |= set([arg for arg in sym_atom.args if isinstance(arg,pddl.Variable)])
00102 return variables
00103
00104 class Fact:
00105 def __init__(self, atom):
00106 self.atom = atom
00107 def __str__(self):
00108 return "%s." % self.atom
00109
00110 class Rule:
00111 def __init__(self, conditions, effect):
00112 self.conditions = conditions
00113 self.effect = effect
00114 def add_condition(self, condition):
00115 self.conditions.append(condition)
00116 def get_variables(self):
00117 return get_variables(self.conditions + [self.effect])
00118 def _rename_duplicate_variables(self, atom, new_conditions):
00119 used_variables = set()
00120 new_args = list(atom.args)
00121 for i, var in enumerate(atom.args):
00122 if isinstance(var,pddl.Variable):
00123 if var in used_variables:
00124 new_var_name = "%s@%d" % (var.name, len(new_conditions))
00125 new_var = pddl.Variable(new_var_name)
00126 new_args[i] = new_var
00127 new_conditions.append(pddl.Atom("=", [var, new_var]))
00128 else:
00129 used_variables.add(var)
00130 atom.args = tuple(new_args)
00131 def rename_duplicate_variables(self):
00132 new_conditions = []
00133 self._rename_duplicate_variables(self.effect, new_conditions)
00134 for condition in self.conditions:
00135 self._rename_duplicate_variables(condition, new_conditions)
00136 self.conditions += new_conditions
00137 return bool(new_conditions)
00138 def __str__(self):
00139 cond_str = ", ".join(map(str, self.conditions))
00140 return "%s :- %s." % (self.effect, cond_str)
00141
00142 def translate_typed_object(prog, obj, type_dict):
00143 supertypes = type_dict[obj.type].supertype_names
00144 for type_name in [obj.type] + supertypes:
00145 prog.add_fact(pddl.Atom(type_name, [pddl.ObjectTerm(obj.name)]))
00146
00147 def translate_init(prog,fact):
00148 if isinstance(fact,pddl.Atom):
00149 prog.add_fact(fact)
00150 elif isinstance(fact,pddl.FunctionAssignment):
00151 atom = normalize.get_function_predicate(fact.fluent)
00152 prog.add_fact(atom)
00153 else:
00154 assert False
00155
00156 def translate_facts(prog, task):
00157 type_dict = dict((type.name, type) for type in task.types)
00158 for obj in task.objects:
00159 translate_typed_object(prog, obj, type_dict)
00160 for fact in task.init:
00161 translate_init(prog,fact)
00162
00163 def translate(task):
00164 normalize.normalize(task)
00165 prog = PrologProgram()
00166 translate_facts(prog, task)
00167 for conditions, effect in normalize.build_exploration_rules(task):
00168 prog.add_rule(Rule(conditions, effect))
00169 prog.normalize()
00170 prog.split_rules()
00171 return prog
00172
00173 def test_normalization():
00174 prog = PrologProgram()
00175 prog.add_fact(pddl.Atom("at", ["foo", "bar"]))
00176 prog.add_fact(pddl.Atom("truck", ["bollerwagen"]))
00177 prog.add_fact(pddl.Atom("truck", ["segway"]))
00178 prog.add_rule(Rule([pddl.Atom("truck", ["?X"])], pddl.Atom("at", ["?X", "?Y"])))
00179 prog.add_rule(Rule([pddl.Atom("truck", ["X"]), pddl.Atom("location", ["?Y"])],
00180 pddl.Atom("at", ["?X", "?Y"])))
00181 prog.add_rule(Rule([pddl.Atom("truck", ["?X"]), pddl.Atom("location", ["?Y"])],
00182 pddl.Atom("at", ["?X", "?X"])))
00183 prog.add_rule(Rule([pddl.Atom("p", ["?Y", "?Z", "?Y", "?Z"])],
00184 pddl.Atom("q", ["?Y", "?Y"])))
00185 prog.add_rule(Rule([], pddl.Atom("foo", [])))
00186 prog.add_rule(Rule([], pddl.Atom("bar", ["X"])))
00187 prog.normalize()
00188 prog.dump()
00189
00190 if __name__ == "__main__":
00191
00192
00193 task = pddl.open()
00194 prog = translate(task)
00195 prog.dump()