normalize.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 # -*- coding: utf-8 -*-
00003 
00004 import copy
00005 
00006 import pddl
00007 
00008 class ConditionProxy(object):
00009   def clone_owner(self):
00010     clone = copy.copy(self)
00011     clone.owner = copy.copy(clone.owner)
00012     return clone
00013     
00014 class PreconditionProxy(ConditionProxy):
00015   def __init__(self, action):
00016     self.owner = action
00017     self.condition = action.condition
00018   def set(self, new_condition):
00019     self.owner.condition = self.condition = new_condition
00020   def register_owner(self, task):
00021     if isinstance(self.owner,pddl.DurativeAction):
00022         task.durative_actions.append(self.owner)
00023     else:
00024         task.actions.append(self.owner)
00025   def delete_owner(self, task):
00026     if isinstance(self.owner,pddl.DurativeAction):
00027         task.durative_actions.remove(self.owner)
00028     else:
00029         task.actions.remove(self.owner)
00030   def build_rules(self, rules, fluent_preds, modules):
00031     action = self.owner
00032     for param in action.parameters:
00033         add_either_rules(param.type,rules)
00034 
00035     rule_head = get_action_predicate(action)
00036 
00037     # we completely use the start conditions.
00038     # from the other conditions, we use only
00039     # the types of existential parameters and
00040     # non-fluent atoms
00041     if isinstance(self.condition,list):
00042         rule_body = list(condition_to_rule_body(action.parameters,
00043                                       self.condition[0]))
00044         rule_body += list(condition_to_rule_body(action.parameters,
00045                                      self.condition[1],fluent_preds))
00046         rule_body += list(condition_to_rule_body(action.parameters,
00047                                      self.condition[2], fluent_preds))
00048     else: 
00049         rule_body = list(condition_to_rule_body(action.parameters,
00050                                             self.condition))
00051     rules.append((rule_body, rule_head))
00052   def get_type_map(self):
00053     return self.owner.type_map
00054 
00055 class EffectConditionProxy(ConditionProxy):
00056   def __init__(self, action, effect, effecttime=None):
00057     self.action = action
00058     self.owner = effect
00059     self.condition = effect.condition
00060     self.effecttime = effecttime
00061   def set(self, new_condition):
00062     self.owner.condition = self.condition = new_condition
00063   def register_owner(self, task):
00064     self.action.effects.append(self.owner)
00065   def delete_owner(self, task):
00066     self.action.effects.remove(self.owner)
00067   def build_rules(self, rules, fluent_preds, modules):
00068     effect = self.owner
00069     rule_head = effect.peffect
00070     # FIXME ModuleCalls do not have any effect (for now)
00071     # possibly add the affected fluents here?
00072     fluent_head = None
00073     module_heads = []
00074     if not isinstance(rule_head,pddl.NegatedAtom):
00075       if isinstance(rule_head, pddl.ModuleCall):
00076         # Determine the fluents this module sets
00077         my_module = None
00078         for module in modules:
00079           if module.name == rule_head.name:
00080             my_module = module
00081             break
00082         if my_module:
00083           # we need to rename all args, s.th. the module effect's
00084           # fluents args are the same as in the modulecall.
00085           assert len(my_module.parameters) == len(rule_head.args)
00086           renamings = {}
00087           for param, arg in zip(my_module.parameters, rule_head.args):
00088             pVar = pddl.Variable(param.name)
00089             renamings[pVar] = arg
00090           # and add a function and fluent predicate for each
00091           for fluent in my_module.effects:
00092             new_fluent = fluent.rename_variables(renamings)
00093             module_heads.append(get_function_predicate(new_fluent))
00094             module_heads.append(get_fluent_function_predicate(new_fluent))
00095         
00096         # finally disable the original single head.
00097         rule_head = None
00098       elif isinstance(rule_head,pddl.FunctionAssignment):
00099         fluent = rule_head.fluent
00100         rule_head = get_function_predicate(fluent)
00101         fluent_head = get_fluent_function_predicate(fluent)
00102       rule_body = [get_action_predicate(self.action)]
00103       if self.effecttime != None:
00104         # we use the start condition in any case
00105         rule_body += condition_to_rule_body([], self.condition[0])
00106         # for end effects we use all conditions
00107         if self.effecttime == 1:
00108           rule_body += condition_to_rule_body([], self.condition[1])
00109           rule_body += condition_to_rule_body([], self.condition[2])
00110       else:
00111         rule_body += condition_to_rule_body([], self.condition)
00112       if rule_head:
00113         rules.append((rule_body, rule_head))
00114       if fluent_head:
00115         rules.append((rule_body, fluent_head))
00116       for head in module_heads:
00117         rules.append((rule_body, head))
00118 
00119   def get_type_map(self):
00120     return self.action.type_map
00121 
00122 class AxiomConditionProxy(ConditionProxy):
00123   def __init__(self, axiom):
00124     self.owner = axiom
00125     self.condition = axiom.condition
00126   def set(self, new_condition):
00127     self.owner.condition = self.condition = new_condition
00128   def register_owner(self, task):
00129     task.axioms.append(self.owner)
00130   def delete_owner(self, task):
00131     task.axioms.remove(self.owner)
00132   def build_rules(self, rules, fluent_preds, modules):
00133     axiom = self.owner
00134     app_rule_head = get_axiom_predicate(axiom)
00135     app_rule_body = list(condition_to_rule_body(axiom.parameters, self.condition))
00136     rules.append((app_rule_body, app_rule_head))
00137     eff_rule_head = pddl.Atom(axiom.name, [pddl.Variable(par.name) for par in axiom.parameters])
00138     eff_rule_body = [app_rule_head]
00139     rules.append((eff_rule_body, eff_rule_head))
00140   def get_type_map(self):
00141     return self.owner.type_map
00142 
00143 class GoalConditionProxy(ConditionProxy):
00144   def __init__(self, task):
00145     self.owner = task
00146     self.condition = task.goal
00147   def set(self, new_condition):
00148     self.owner.goal = self.condition = new_condition
00149   def register_owner(self, task):
00150     # TODO: Implement with axioms.
00151     assert False, "Disjunctive goals not (yet) implemented."
00152   def delete_owner(self, task):
00153     # TODO: Implement with axioms.
00154     assert False, "Disjunctive goals not (yet) implemented."
00155   def build_rules(self, rules, fluent_preds, modules):
00156     rule_head_name = "@goal-reachable"
00157     rule_head = pddl.Atom("@goal-reachable", [])
00158     rule_body = list(condition_to_rule_body([], self.condition))
00159     rules.append((rule_body, rule_head))
00160   def get_type_map(self):
00161     # HACK!
00162     # Method uniquify_variables HAS already been called (which is good).
00163     # We call it here again for its SIDE EFFECT of collecting the type_map
00164     # (which is bad). Having "top-level conditions" (currently, only goal
00165     # conditions, but might also include safety conditions and similar)
00166     # contained in a separate wrapper class that stores a type map might
00167     # be a better design.
00168     type_map = {}
00169     self.condition.uniquify_variables(type_map)
00170     return type_map
00171 
00172 def get_action_predicate(action):
00173   name = action
00174   variables = [pddl.Variable(par.name) for par in action.parameters]
00175   if isinstance(action.condition,list):
00176     for condition in action.condition:
00177       if isinstance(condition, pddl.ExistentialCondition):
00178         variables += [pddl.Variable(par.name) for par in condition.parameters]
00179   if isinstance(action.condition, pddl.ExistentialCondition):
00180     variables += [pddl.Variable(par.name) for par in action.condition.parameters]
00181   return pddl.Atom(name, variables)
00182 
00183 def get_axiom_predicate(axiom):
00184   name = axiom
00185   variables = [pddl.Variable(par.name) for par in axiom.parameters]
00186   if isinstance(axiom.condition, pddl.ExistentialCondition):
00187     variables += [pddl.Variable(par.name) for par in axiom.condition.parameters]
00188   return pddl.Atom(name, variables)
00189 
00190 def all_conditions(task):
00191   for action in task.actions:
00192     yield PreconditionProxy(action)
00193     for effect in action.effects:
00194       yield EffectConditionProxy(action, effect)
00195   for action in task.durative_actions:
00196     yield PreconditionProxy(action)
00197     for time,timedeffects in enumerate(action.effects):
00198         for effect in timedeffects:
00199             yield EffectConditionProxy(action, effect, time)
00200   for axiom in task.axioms:
00201     yield AxiomConditionProxy(axiom)
00202   yield GoalConditionProxy(task)
00203 
00204 # [1] Remove universal quantifications from conditions.
00205 #
00206 # Replace, in a top-down fashion, <forall(vars, phi)> by <not(not-all-phi)>,
00207 # where <not-all-phi> is a new axiom.
00208 #
00209 # <not-all-phi> is defined as <not(forall(vars,phi))>, which is of course
00210 # translated to NNF. The parameters of the new axioms are exactly the free
00211 # variables of <forall(vars, phi)>.
00212 
00213 def remove_universal_quantifiers(task):
00214   def recurse(condition):
00215     # Uses new_axioms_by_condition and type_map from surrounding scope.
00216     if isinstance(condition, pddl.UniversalCondition):
00217       axiom_condition = condition.negate()
00218       parameters = axiom_condition.free_variables()
00219       axiom = new_axioms_by_condition.get(axiom_condition)
00220       if not axiom:
00221         typed_parameters = [pddl.TypedObject(v, type_map[v]) for v in parameters]
00222         condition = recurse(axiom_condition)
00223         axiom = task.add_axiom(typed_parameters, condition)
00224         new_axioms_by_condition[condition] = axiom
00225       return pddl.NegatedAtom(axiom.name, [pddl.conditions.parse_term(par) for par in parameters]) 
00226     else:
00227       new_parts = [recurse(part) for part in condition.parts]
00228       return condition.change_parts(new_parts)
00229 
00230   new_axioms_by_condition = {}
00231   for proxy in tuple(all_conditions(task)):
00232     # Cannot use generator because we add new axioms on the fly.
00233     if isinstance(proxy.condition,list):
00234       change = False
00235       condition = []
00236       for cond in proxy.condition:
00237         if cond.has_universal_part():
00238           if not change:
00239             change = True
00240             type_map = proxy.get_type_map()
00241           condition.append(recurse(cond))
00242         else:
00243           condition.append(cond)
00244       if change:
00245         proxy.set(condition)
00246     elif proxy.condition.has_universal_part():
00247       type_map = proxy.get_type_map()
00248       proxy.set(recurse(proxy.condition))
00249 
00250     
00251 # [2] Pull disjunctions to the root of the condition.
00252 #
00253 # After removing universal quantifiers, the (k-ary generalization of the)
00254 # following rules suffice for doing that: 
00255 # (1) or(phi, or(psi, psi'))      ==  or(phi, psi, psi')
00256 # (2) exists(vars, or(phi, psi))  ==  or(exists(vars, phi), exists(vars, psi))
00257 # (3) and(phi, or(psi, psi'))     ==  or(and(phi, psi), and(phi, psi'))
00258 def build_DNF(task):
00259   def recurse(condition):
00260     disjunctive_parts = []
00261     other_parts = []
00262     for part in condition.parts:
00263       part = recurse(part)
00264       if isinstance(part, pddl.Disjunction):
00265         disjunctive_parts.append(part)
00266       else:
00267         other_parts.append(part)
00268     if not disjunctive_parts:
00269       return condition
00270 
00271     # Rule (1): Associativity of disjunction.
00272     if isinstance(condition, pddl.Disjunction):
00273       result_parts = other_parts
00274       for part in disjunctive_parts:
00275         result_parts.extend(part.parts)
00276       return pddl.Disjunction(result_parts)
00277 
00278     # Rule (2): Distributivity disjunction/existential quantification.
00279     if isinstance(condition, pddl.ExistentialCondition):
00280       parameters = condition.parameters
00281       result_parts = [pddl.ExistentialCondition(parameters, (part,))
00282                       for part in disjunctive_parts[0].parts]
00283       return pddl.Disjunction(result_parts)
00284 
00285     # Rule (3): Distributivity disjunction/conjunction.
00286     assert isinstance(condition, pddl.Conjunction)
00287     result_parts = [pddl.Conjunction(other_parts)]
00288     while disjunctive_parts:
00289       previous_result_parts = result_parts
00290       result_parts = []
00291       parts_to_distribute = disjunctive_parts.pop().parts
00292       for part1 in previous_result_parts:
00293         for part2 in parts_to_distribute:
00294           result_parts.append(pddl.Conjunction((part1, part2)))
00295     return pddl.Disjunction(result_parts)
00296 
00297   for proxy in all_conditions(task):
00298     if isinstance(proxy.condition,list):
00299       condition = []
00300       for cond in proxy.condition:
00301         if cond.has_disjunction():
00302           condition.append(recurse(cond).simplified())
00303         else:
00304           condition.append(cond.simplified())
00305       proxy.set(condition)
00306     elif proxy.condition.has_disjunction():
00307       proxy.set(recurse(proxy.condition).simplified())
00308     else:
00309       proxy.set(proxy.condition.simplified())
00310 
00311 # [3] Split conditions at the outermost disjunction.
00312 def split_disjunctions(task):
00313   for proxy in tuple(all_conditions(task)):
00314     # Cannot use generator directly because we add/delete entries.
00315     if isinstance(proxy.condition,list):
00316       change = False
00317       conditions = [[]]
00318       for cond in proxy.condition:
00319         if isinstance(cond, pddl.Disjunction):
00320           change = True
00321           old_conditions = conditions
00322           conditions = []
00323           for part in cond.parts:
00324             for condition in old_conditions:
00325               new_condition = copy.copy(condition)
00326               new_condition.append(part)
00327               conditions.append(new_condition)
00328         else:
00329           for condition in conditions:
00330             condition.append(cond)
00331       if change:
00332         for condition in conditions:
00333           new_proxy = proxy.clone_owner()
00334           new_proxy.set(condition)
00335           new_proxy.register_owner(task)
00336         proxy.delete_owner(task)
00337     elif isinstance(proxy.condition, pddl.Disjunction):
00338       for part in proxy.condition.parts:
00339         new_proxy = proxy.clone_owner()
00340         new_proxy.set(part)
00341         new_proxy.register_owner(task)
00342       proxy.delete_owner(task)
00343 
00344 # [4] Pull existential quantifiers out of conjunctions and group them.
00345 #
00346 # After removing universal quantifiers and creating the disjunctive form,
00347 # only the following (representatives of) rules are needed:
00348 # (1) exists(vars, exists(vars', phi))  ==  exists(vars + vars', phi)
00349 # (2) and(phi, exists(vars, psi))       ==  exists(vars, and(phi, psi)),
00350 #       if var does not occur in phi as a free variable.
00351 def move_existential_quantifiers(task):
00352   def recurse(condition):
00353     existential_parts = []
00354     other_parts = []
00355     for part in condition.parts:
00356       part = recurse(part)
00357       if isinstance(part, pddl.ExistentialCondition):
00358         existential_parts.append(part)
00359       else:
00360         other_parts.append(part)
00361     if not existential_parts:
00362       return condition
00363 
00364     # Rule (1): Combine nested quantifiers.
00365     if isinstance(condition, pddl.ExistentialCondition):
00366       new_parameters = condition.parameters + existential_parts[0].parameters
00367       new_parts = existential_parts[0].parts
00368       return pddl.ExistentialCondition(new_parameters, new_parts)
00369 
00370     # Rule (2): Pull quantifiers out of conjunctions.
00371     assert isinstance(condition, pddl.Conjunction)
00372     new_parameters = []
00373     new_conjunction_parts = other_parts
00374     for part in existential_parts:
00375       new_parameters += part.parameters
00376       new_conjunction_parts += part.parts
00377     new_conjunction = pddl.Conjunction(new_conjunction_parts)
00378     return pddl.ExistentialCondition(new_parameters, (new_conjunction,))
00379 
00380   for proxy in all_conditions(task):
00381     if isinstance(proxy.condition,list):
00382       condition = []
00383       for cond in proxy.condition:
00384         if cond.has_existential_part():
00385           condition.append(recurse(cond).simplified())
00386         else:
00387           condition.append(cond)
00388       proxy.set(condition)
00389     elif proxy.condition.has_existential_part():
00390       proxy.set(recurse(proxy.condition).simplified())
00391 
00392 def remove_object_functions_from_durations(task):
00393     for act in task.durative_actions:
00394         used_variables = [var.name for var in act.parameters]
00395         for time in range(2):
00396             for index, (op, exp) in enumerate(act.duration[time]):
00397                 typed_vars, function_terms, new_term = \
00398                     exp.compile_objectfunctions_aux(used_variables, 
00399                         recurse_object_terms=False)
00400                 act.duration[time][index] = (op, new_term)
00401                 act.parameters += typed_vars
00402                 new_conditions = []
00403                 assert len(typed_vars) == len(function_terms)
00404                 new_conditions = [act.condition[time]]
00405                 for var, term in zip(typed_vars, function_terms):
00406                     variable = pddl.Variable(var.name)
00407                     new_condition = pddl.Atom("=", [variable, term])
00408                     new_conditions.append(new_condition)
00409                 act.condition[time] = pddl.Conjunction(new_conditions)
00410 
00411 
00412 def remove_object_functions(task):
00413     def recurse(condition, used_variables):
00414         if isinstance(condition, pddl.Literal):
00415             typed_vars = []
00416             conjunction_parts = []
00417             new_args = []
00418             for term in condition.args:
00419                 typed,parts,new_term = term.compile_objectfunctions_aux(used_variables)
00420                 typed_vars += typed
00421                 conjunction_parts += parts
00422                 new_args.append(new_term)
00423             if conjunction_parts == []:
00424                 return condition
00425             else:
00426                 new_literal = condition.__class__(condition.predicate,new_args)
00427                 conjunction_parts.append(new_literal)
00428                 conjunction = pddl.Conjunction(conjunction_parts)
00429                 return pddl.ExistentialCondition(typed_vars,[conjunction])
00430         elif isinstance(condition, pddl.ModuleCall):
00431             # FIXME no idea what this does, but hopefully the right thing...
00432             # This is only for object fluents?
00433             # So probably I don't need this at all and can
00434             # fallback to default? (return condition?)
00435             typed_vars = []
00436             conjunction_parts = []
00437             new_args = []
00438             for term in condition.args:
00439                 typed,parts,new_term = term.compile_objectfunctions_aux(used_variables)
00440                 typed_vars += typed
00441                 conjunction_parts += parts
00442                 new_args.append(new_term)
00443             if conjunction_parts == []:
00444                 return condition
00445             else:
00446                 new_literal = condition.__class__(condition.name, new_args)
00447                 conjunction_parts.append(new_literal)
00448                 conjunction = pddl.Conjunction(conjunction_parts)
00449                 return pddl.ExistentialCondition(typed_vars,[conjunction])
00450         elif isinstance(condition, pddl.FunctionComparison):
00451             typed_vars = []
00452             conjunction_parts = []
00453             new_parts = []
00454             for part in condition.parts:
00455                 typed,parts,new_part = part.compile_objectfunctions_aux(used_variables)
00456                 typed_vars += typed
00457                 conjunction_parts += parts
00458                 new_parts.append(new_part)
00459             if conjunction_parts == []:
00460                 return condition
00461             else:
00462                 new_comparison = condition.__class__(condition.comparator,new_parts)
00463                 conjunction_parts.append(new_comparison)
00464                 conjunction = pddl.Conjunction(conjunction_parts)
00465                 return pddl.ExistentialCondition(typed_vars,[conjunction])
00466         else:
00467             new_parts = [recurse(part,used_variables) for part in condition.parts]
00468             return condition.change_parts(new_parts)
00469 
00470 # TODO: Handle cost modules   
00471 #    remove_object_functions_from_durations(task)
00472 
00473     for proxy in tuple(all_conditions(task)):
00474         if isinstance(proxy.condition,list):
00475             condition = []
00476             used_variables = set()
00477             for cond in proxy.condition:
00478                 used_variables |= cond.free_variables()
00479             used_variables = list(used_variables)
00480             for cond in proxy.condition:
00481                 condition.append(recurse(cond,used_variables))
00482             proxy.set(condition)
00483         else:
00484             used_variables = list(proxy.condition.free_variables())
00485             proxy.set(recurse(proxy.condition,used_variables))
00486 
00487 def remove_duration_variable(task):
00488     def recurse(condition, act, time, duration, pnes):
00489         if isinstance(condition, pddl.FunctionComparison):
00490             parts = [exp.remove_duration_variable(act, time, duration, pnes)
00491                         for exp in condition.parts]
00492             return pddl.FunctionComparison(condition.comparator,parts)
00493             return comp       # FIXME: Is this line ever called? - remove?
00494         else:
00495             new_parts = [recurse(part, act, time, duration, pnes) for part in condition.parts]
00496             return condition.change_parts(new_parts)
00497 
00498     for act in task.durative_actions:
00499         assert len(act.duration[1]) == 0, "at end durations are not supported"
00500         assert len(act.duration[0]) == 1 and act.duration[0][0][0]=="="
00501         duration = act.duration[0][0][1]
00502         duration_functions = []
00503 
00504         # remove from action conditions
00505         condition = []
00506         for time, cond in enumerate(act.condition):
00507             condition.append(recurse(cond, act, time, duration, duration_functions))
00508         act.condition = condition
00509 
00510         for time in range(2):
00511             for eff in act.effects[time]:
00512                 # remove from effect condition
00513                 condition = []
00514                 for eff_time, cond in enumerate(eff.condition):
00515                     condition.append(recurse(cond, act, eff_time, duration, duration_functions))
00516                 eff.condition = condition
00517                 # remove from effect
00518                 if isinstance(eff.peffect,pddl.FunctionAssignment):
00519                     assign = eff.peffect
00520                     assign.expression = assign.expression.remove_duration_variable(act, 
00521                                                     time, duration, duration_functions)
00522         for pne in duration_functions:
00523             assign = pddl.Assign(pne,duration)
00524             condition = [pddl.Truth(),pddl.Truth(),pddl.Truth()]
00525             effect = pddl.Effect([],condition, assign)
00526             act.effects[0].append(effect)
00527             task.function_symbols[pne.symbol]="number"
00528 
00529 
00530 def remove_arithmetic_expressions(task):
00531     def recurse(condition):
00532         if isinstance(condition, pddl.FunctionComparison):
00533             parts = [task.function_administrator.get_derived_function(exp)
00534                         for exp in condition.parts]
00535             if condition.negated:
00536                 return pddl.NegatedFunctionComparison(condition.comparator,parts)
00537             else:
00538                 return pddl.FunctionComparison(condition.comparator,parts)
00539         else:
00540             new_parts = [recurse(part) for part in condition.parts]
00541             return condition.change_parts(new_parts)
00542 
00543     # remove from conditions
00544     for proxy in tuple(all_conditions(task)):
00545         if isinstance(proxy.condition,list):
00546             condition = []
00547             for cond in proxy.condition:
00548                 condition.append(recurse(cond))
00549             proxy.set(condition)
00550         else:
00551             proxy.set(recurse(proxy.condition))
00552 
00553     # remove from actions
00554     admin = task.function_administrator
00555     for act in task.actions:
00556         for eff in act.effects:
00557             if isinstance(eff.peffect,pddl.FunctionAssignment):
00558                 assign = eff.peffect
00559                 assign.expression = admin.get_derived_function(assign.expression)
00560     for act in task.durative_actions:
00561         dur0 = []
00562         dur1 = []
00563         for (op,exp) in act.duration[0]:
00564           if isinstance(exp, pddl.ModuleCall):
00565             dur0.append((op, exp))
00566           else:
00567             dur0.append((op, task.function_administrator.get_derived_function(exp)))
00568         for (op,exp) in act.duration[1]:
00569           if isinstance(exp, pddl.ModuleCall):
00570             dur1.append((op, exp))
00571           else:
00572             dur1.append((op, task.function_administrator.get_derived_function(exp)))
00573         act.duration = [dur0, dur1]
00574         for tempeff in act.effects:
00575             for eff in tempeff:
00576                 if isinstance(eff.peffect,pddl.FunctionAssignment):
00577                     assign = eff.peffect
00578                     assign.expression = admin.get_derived_function(assign.expression)
00579 
00580 def substitute_complicated_goal(task):
00581   goal = task.goal
00582   if isinstance(goal, pddl.Literal):
00583     return
00584   elif isinstance(goal,pddl.Conjunction):
00585     simple_goal = True
00586     for item in goal.parts:
00587       if not isinstance(item,pddl.Literal):
00588         simple_goal = False
00589         break
00590     if simple_goal:
00591       return
00592   new_axiom = task.add_axiom([],goal)
00593   task.goal = pddl.Atom(new_axiom.name, new_axiom.parameters)
00594             
00595 # Combine Steps [1], [2], [3], [4]
00596 def normalize(task):
00597   remove_object_functions(task)
00598   substitute_complicated_goal(task)
00599   remove_universal_quantifiers(task)
00600   build_DNF(task)
00601   split_disjunctions(task)
00602   move_existential_quantifiers(task)
00603   remove_duration_variable(task)
00604   remove_arithmetic_expressions(task)
00605 
00606 # [5] Build rules for exploration component.
00607 def build_exploration_rules(task):
00608   result = []
00609   fluent_preds = get_fluent_predicates(task)
00610   
00611   for proxy in all_conditions(task):
00612     proxy.build_rules(result, fluent_preds, task.modules)
00613 
00614   for axiom in task.function_administrator.get_all_axioms():
00615     # add rules to determine defined functions
00616     rule_head = get_function_axiom_predicate(axiom)
00617     rule_body = []
00618     for part in axiom.parts:
00619         if isinstance(part,pddl.PrimitiveNumericExpression):
00620             rule_body.append(get_function_predicate(part))
00621             
00622     result.append((rule_body, rule_head))
00623     rule_body = [rule_head]
00624     rule_head = get_function_predicate(axiom.get_head())
00625     result.append((rule_body, rule_head))
00626 
00627     # add rule to determine fluent functions
00628     rule_head = get_fluent_function_predicate(axiom.get_head())
00629     for part in axiom.parts:
00630         if isinstance(part,pddl.PrimitiveNumericExpression):
00631             new_rule_body = rule_body + [get_fluent_function_predicate(part)]
00632             result.append((new_rule_body, rule_head))
00633   return result
00634 
00635 def condition_to_rule_body(parameters, condition, fluent_preds = None):
00636   for par in parameters:
00637     yield pddl.Atom(par.type, [pddl.Variable(par.name)])
00638   if not isinstance(condition, pddl.Truth):
00639     if isinstance(condition, pddl.ExistentialCondition):
00640       for par in condition.parameters:
00641         yield pddl.Atom(par.type, [pddl.Variable(par.name)])
00642       condition = condition.parts[0]
00643     if isinstance(condition, pddl.Conjunction):
00644       parts = condition.parts
00645     else:
00646       parts = (condition,)
00647     for part in parts:
00648       # FIXME Modulecall might be at lowest level
00649       assert isinstance(part, pddl.Literal) or isinstance(part,pddl.FunctionComparison) or isinstance(part, pddl.ModuleCall), "Condition not normalized"
00650       if isinstance(part, pddl.Literal):
00651         if not part.negated:
00652             if fluent_preds == None or part.predicate not in fluent_preds:
00653                 yield part
00654       elif isinstance(part, pddl.ModuleCall):
00655         # FIXME: yield nothing for that?
00656         # FIXME: this might be wrong - need to check, if rules are used
00657         # to build the task, then we would remove this wrongly
00658         # BUT this should be equivalent to TRUTH in the rule body
00659         # Alternativ: Make one of these "Atoms" (being true and holding module as "owner")
00660         # howevery they are used...
00661         # I guess we might wanna yield a truth "ModuleAtom" here...
00662         # or CAN WE IGNORE THIS?
00663         # there are also only non-negated literals in here...
00664         pass
00665       elif fluent_preds == None: # part is FunctionComparison
00666         primitives = part.primitive_numeric_expressions()
00667         for pne in primitives:
00668             yield get_function_predicate(pne)
00669 
00670 def get_function_predicate(pne):
00671   name = "defined!%s" % pne.symbol
00672   return pddl.Atom(name, pne.args)
00673 
00674 def get_fluent_function_predicate(pne):
00675   return pddl.Atom(pne,pne.args)
00676 
00677 def get_function_axiom_predicate(axiom):
00678   name = axiom
00679   args = axiom.parameters
00680   for part in axiom.parts:
00681     if isinstance(part, pddl.PrimitiveNumericExpression):
00682         args += part.args
00683     elif isinstance(part, pddl.NumericAxiom):
00684         args += part.parameters
00685   return pddl.Atom(name, args)
00686 
00687 def get_fluent_predicates(task):
00688   fluent_predicates = set()
00689   for action in task.actions:
00690     for effect in action.effects:
00691       if isinstance(effect.peffect,pddl.Literal):
00692         fluent_predicates.add(effect.peffect.predicate)
00693       elif isinstance(effect.peffect, pddl.ModuleCall):
00694         pass
00695       else:
00696         predicate = get_function_predicate(effect.peffect.fluent).predicate
00697         fluent_predicates.add(predicate)
00698   for action in task.durative_actions:
00699     for effect in action.effects:
00700         for eff in effect:
00701           if isinstance(eff.peffect,pddl.Literal):
00702             fluent_predicates.add(eff.peffect.predicate)
00703           elif isinstance(eff.peffect, pddl.ModuleCall):
00704             # FIXME: A module effect does nothing (for the view of the planner)
00705             # Or should we add the affected fluents here? Depends on what this is used for...
00706             pass
00707           else:
00708             predicate = get_function_predicate(eff.peffect.fluent).predicate
00709             fluent_predicates.add(predicate)
00710   for axiom in task.axioms:
00711     fluent_predicates.add(axiom.name)
00712   return fluent_predicates 
00713 
00714 def add_either_rules(type,rules):
00715   if isinstance(type,tuple):
00716     assert type[0]=="either"
00717     for subtype in type[1:]:
00718       add_either_rules(subtype,rules)
00719       rule_head = pddl.Atom(type, [pddl.Variable("?x")])
00720       rule_body = [pddl.Atom(subtype, [pddl.Variable("?x")])]
00721       rules.append((rule_body, rule_head))
00722 
00723 if __name__ == "__main__":
00724   task = pddl.open()
00725   normalize(task)
00726   task.dump()


tfd_modules
Author(s): Maintained by Christian Dornhege (see AUTHORS file).
autogenerated on Mon Oct 6 2014 07:52:06