pddl_to_prolog.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 # -*- coding: latin-1 -*-
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     # Normalized prolog programs have the following properties:
00030     # 1. Each variable that occurs in the effect of a rule also occurs in its
00031     #    condition. 
00032     # 2. The variables that appear in each effect or condition are distinct.
00033     # 3. There are no rules with empty condition.
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     # Splits rules whose conditions can be partitioned in such a way that
00040     # the parts have disjoint variable sets, then split n-ary joins into
00041     # a number of binary joins, introducing new pseudo-predicates for the
00042     # intermediate values.
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     # Note: This should never be necessary for typed domains.
00055     # Leaving it in at the moment regardless.
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     #variables |= set([arg for arg in sym_atom.args if arg[0] == "?"])
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   # test_normalization()
00192 
00193   task = pddl.open()
00194   prog = translate(task)
00195   prog.dump()
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines


tfd_modules
Author(s): Maintained by Christian Dornhege (see AUTHORS file).
autogenerated on Tue Jan 22 2013 12:25:03