00001 import itertools
00002
00003 import pddl_types
00004 import f_expression
00005 import tasks
00006
00007 def parse_condition(alist):
00008 condition = parse_condition_aux(alist, False)
00009 return condition
00010
00011 def parse_condition_aux(alist, negated):
00012 """Parse a PDDL condition. The condition is translated into NNF on the fly."""
00013 tag = alist[0]
00014
00015 if is_function_comparison(alist):
00016 args = [f_expression.parse_expression(arg) for arg in alist[1:]]
00017 assert len(args) == 2, args
00018 if negated:
00019 return NegatedFunctionComparison(tag, args, True)
00020 else:
00021 return FunctionComparison(tag, args, True)
00022 elif tag in ("and", "or", "not", "imply"):
00023 args = alist[1:]
00024 if tag == "imply":
00025 assert len(args) == 2
00026 if tag == "not":
00027 assert len(args) == 1
00028 return parse_condition_aux(args[0], not negated)
00029 elif tag in ("forall", "exists"):
00030 parameters = pddl_types.parse_typed_list(alist[1])
00031 args = alist[2:]
00032 assert len(args) == 1
00033 elif tag == "[":
00034 assert not negated
00035 assert alist[-1] == "]"
00036 alist = alist[1:-1]
00037
00038
00039
00040 return ModuleCall(alist[0], [parse_term(term) for term in alist[1:]])
00041 elif negated:
00042 return NegatedAtom(alist[0], [parse_term(term) for term in alist[1:]])
00043 else:
00044 return Atom(alist[0],[parse_term(term) for term in alist[1:]])
00045
00046
00047 if tag == "imply":
00048 parts = [parse_condition_aux(args[0], not negated),
00049 parse_condition_aux(args[1], negated)]
00050 tag = "or"
00051 else:
00052 parts = [parse_condition_aux(part, negated) for part in args]
00053 if tag == "and" and not negated or tag == "or" and negated:
00054 return Conjunction(parts)
00055 elif tag == "or" and not negated or tag == "and" and negated:
00056 return Disjunction(parts)
00057 elif tag == "forall" and not negated or tag == "exists" and negated:
00058 return UniversalCondition(parameters, parts)
00059 elif tag == "exists" and not negated or tag == "forall" and negated:
00060 return ExistentialCondition(parameters, parts)
00061
00062 def parse_durative_condition(alist):
00063 """Parse a durative PDDL condition. i
00064 The condition is translated into NNF on the fly.
00065 Returns triple [start condition, over all condition, end condition]"""
00066 if len(alist)==0:
00067 return [Truth(), Truth(),Truth()]
00068 tag = alist[0]
00069 if tag == "and":
00070 args = alist[1:]
00071 parts = [parse_durative_condition(part) for part in args]
00072 parts_begin = [part[0] for part in parts]
00073 parts_end = [part[1] for part in parts]
00074 parts_all = [part[2] for part in parts]
00075 return [Conjunction(parts_begin),Conjunction(parts_end),Conjunction(parts_all)]
00076 elif tag == "forall":
00077 parameters = pddl_types.parse_typed_list(alist[1])
00078 args = alist[2:]
00079 assert len(args) == 1
00080 parts = [parse_durative_condition(part) for part in args]
00081 parts_begin = [part[0] for part in parts]
00082 parts_end = [part[1] for part in parts]
00083 parts_all = [part[2] for part in parts]
00084 return [UniversalCondition(parameters, parts_begin),
00085 UniversalCondition(parameters, parts_end),
00086 UniversalCondition(parameters, parts_all)]
00087 elif tag == "at":
00088 assert len(alist) == 3
00089 assert alist[1] in ("start", "end")
00090 condition = parse_condition_aux(alist[2], False)
00091 if alist[1] == "start":
00092 return [condition, Truth(), Truth()]
00093 else:
00094 return [Truth(), Truth(), condition]
00095 elif tag == "over":
00096 assert alist[1] == "all"
00097 assert len(alist) == 3
00098 condition = parse_condition_aux(alist[2], False)
00099 return [Truth(), condition, Truth()]
00100
00101 def is_function_comparison(alist):
00102 tag = alist[0]
00103 if tag in (">","<",">=","<="):
00104 return True
00105 if not tag == "=":
00106 return False
00107
00108
00109 symbol = alist[1]
00110 if isinstance(symbol,list):
00111 if symbol[0] in ("+","/","*","-"):
00112 return True
00113 symbol = symbol[0]
00114 if (tasks.Task.FUNCTION_SYMBOLS.get(symbol,"object")=="number" or
00115 symbol.replace(".","").isdigit()):
00116 return True
00117 return False
00118
00119 def parse_literal(alist):
00120 if alist[0] == "not":
00121 assert len(alist) == 2
00122 alist = alist[1]
00123 return NegatedAtom(alist[0], [parse_term(term) for term in alist[1:]])
00124 else:
00125 return Atom(alist[0], [parse_term(term) for term in alist[1:]])
00126
00127 def parse_term(term):
00128 if isinstance(term, list):
00129 return FunctionTerm(term[0],[parse_term(t) for t in term[1:]])
00130 elif term.startswith("?"):
00131 return Variable(term)
00132 elif term in tasks.Task.FUNCTION_SYMBOLS:
00133 return FunctionTerm(term,[])
00134 else:
00135 return ObjectTerm(term)
00136
00137 def dump_temporal_condition(condition,indent=" "):
00138 assert len(condition)==3
00139 if not isinstance(condition[0],Truth):
00140 print "%sat start:" % indent
00141 condition[0].dump(indent+" ")
00142 if not isinstance(condition[1],Truth):
00143 print "%sover all:" % indent
00144 condition[1].dump(indent+" ")
00145 if not isinstance(condition[2],Truth):
00146 print "%sat end:" % indent
00147 condition[2].dump(indent+" ")
00148
00149
00150
00151
00152
00153
00154
00155
00156 class Condition(object):
00157 def __init__(self, parts):
00158 self.parts = tuple(parts)
00159 self.hash = hash((self.__class__, self.parts))
00160 def __hash__(self):
00161 return self.hash
00162 def __ne__(self, other):
00163 return not self == other
00164 def dump(self, indent=" "):
00165 print "%s%s" % (indent, self._dump())
00166 for part in self.parts:
00167 part.dump(indent + " ")
00168 def _dump(self):
00169 return self.__class__.__name__
00170 def _postorder_visit(self, method_name, *args):
00171 part_results = [part._postorder_visit(method_name, *args)
00172 for part in self.parts]
00173 method = getattr(self, method_name, self._propagate)
00174 return method(part_results, *args)
00175 def _propagate(self, parts, *args):
00176 return self.change_parts(parts)
00177 def simplified(self):
00178 return self._postorder_visit("_simplified")
00179 def relaxed(self):
00180 return self._postorder_visit("_relaxed")
00181 def untyped(self):
00182 return self._postorder_visit("_untyped")
00183
00184 def uniquify_variables(self, type_map, renamings={}):
00185
00186
00187 if not self.parts:
00188 return self
00189 else:
00190 return self.__class__([part.uniquify_variables(type_map, renamings)
00191 for part in self.parts])
00192 def to_untyped_strips(self):
00193 raise ValueError("Not a STRIPS condition: %s" % self.__class__.__name__)
00194 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00195 fluent_functions, task, new_axiom, new_modules, result):
00196 raise ValueError("Cannot instantiate condition: not normalized")
00197 def free_variables(self):
00198 result = set()
00199 for part in self.parts:
00200 result |= part.free_variables()
00201 return result
00202 def has_disjunction(self):
00203 for part in self.parts:
00204 if part.has_disjunction():
00205 return True
00206 return False
00207 def has_existential_part(self):
00208 for part in self.parts:
00209 if part.has_existential_part():
00210 return True
00211 return False
00212 def has_universal_part(self):
00213 for part in self.parts:
00214 if part.has_universal_part():
00215 return True
00216 return False
00217
00218 class ConstantCondition(Condition):
00219 parts = ()
00220 def __init__(self):
00221 self.hash = hash(self.__class__)
00222 def change_parts(self, parts):
00223 return self
00224 def __eq__(self, other):
00225 return self.__class__ is other.__class__
00226
00227 class Impossible(Exception):
00228 pass
00229
00230 class Falsity(ConstantCondition):
00231 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00232 fluent_functions, task, new_axiom, new_modules, result):
00233 raise Impossible()
00234 def negate(self):
00235 return Truth()
00236
00237 class Truth(ConstantCondition):
00238 def to_untyped_strips(self):
00239 return []
00240 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00241 fluent_functions, task, new_axiom, new_modules, result):
00242 pass
00243 def negate(self):
00244 return Falsity()
00245
00246 class JunctorCondition(Condition):
00247 def __eq__(self, other):
00248
00249 return (self.hash == other.hash and
00250 self.__class__ is other.__class__ and
00251 self.parts == other.parts)
00252 def change_parts(self, parts):
00253 return self.__class__(parts)
00254
00255 class Conjunction(JunctorCondition):
00256 def _simplified(self, parts):
00257 result_parts = []
00258 for part in parts:
00259 if isinstance(part, Conjunction):
00260 result_parts += part.parts
00261 elif isinstance(part, Falsity):
00262 return Falsity()
00263 elif not isinstance(part, Truth):
00264 result_parts.append(part)
00265 if not result_parts:
00266 return Truth()
00267 if len(result_parts) == 1:
00268 return result_parts[0]
00269 return Conjunction(result_parts)
00270 def to_untyped_strips(self):
00271 result = []
00272 for part in self.parts:
00273 result += part.to_untyped_strips()
00274 return result
00275 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00276 fluent_functions, task, new_axiom, new_modules, result):
00277 assert not result, "Condition not simplified"
00278 for part in self.parts:
00279 part.instantiate(var_mapping, init_facts, fluent_facts, init_function_vals,
00280 fluent_functions, task, new_axiom, new_modules, result)
00281 def negate(self):
00282 return Disjunction([p.negate() for p in self.parts])
00283
00284 class Disjunction(JunctorCondition):
00285 def _simplified(self, parts):
00286 result_parts = []
00287 for part in parts:
00288 if isinstance(part, Disjunction):
00289 result_parts += part.parts
00290 elif isinstance(part, Truth):
00291 return Truth()
00292 elif not isinstance(part, Falsity):
00293 result_parts.append(part)
00294 if not result_parts:
00295 return Falsity()
00296 if len(result_parts) == 1:
00297 return result_parts[0]
00298 return Disjunction(result_parts)
00299 def negate(self):
00300 return Conjunction([p.negate() for p in self.parts])
00301 def has_disjunction(self):
00302 return True
00303
00304 class QuantifiedCondition(Condition):
00305 def __init__(self, parameters, parts):
00306 self.parameters = tuple(parameters)
00307 self.parts = tuple(parts)
00308 self.hash = hash((self.__class__, self.parameters, self.parts))
00309 def __eq__(self, other):
00310
00311 return (self.hash == other.hash and
00312 self.__class__ is other.__class__ and
00313 self.parameters == other.parameters and
00314 self.parts == other.parts)
00315 def _dump(self, indent=" "):
00316 arglist = ", ".join(map(str, self.parameters))
00317 return "%s %s" % (self.__class__.__name__, arglist)
00318 def _simplified(self, parts):
00319 if isinstance(parts[0], ConstantCondition):
00320 return parts[0]
00321 else:
00322 return self._propagate(parts)
00323 def uniquify_variables(self, type_map, renamings={}):
00324 renamings = dict(renamings)
00325 new_parameters = [par.uniquify_name(type_map, renamings)
00326 for par in self.parameters]
00327 new_parts = (self.parts[0].uniquify_variables(type_map, renamings),)
00328 return self.__class__(new_parameters, new_parts)
00329
00330 def free_variables(self):
00331 result = Condition.free_variables(self)
00332 for par in self.parameters:
00333 result.discard(par.name)
00334 return result
00335 def change_parts(self, parts):
00336 return self.__class__(self.parameters, parts)
00337
00338 class UniversalCondition(QuantifiedCondition):
00339
00340
00341
00342
00343 def negate(self):
00344 return ExistentialCondition(self.parameters, [p.negate() for p in self.parts])
00345 def has_universal_part(self):
00346 return True
00347
00348 class ExistentialCondition(QuantifiedCondition):
00349
00350
00351
00352
00353 def negate(self):
00354 return UniversalCondition(self.parameters, [p.negate() for p in self.parts])
00355 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00356 fluent_functions, task, new_axiom, new_modules, result):
00357 assert not result, "Condition not simplified"
00358 self.parts[0].instantiate(var_mapping, init_facts, fluent_facts, init_function_vals,
00359 fluent_functions, task, new_axiom, new_modules, result)
00360 def has_existential_part(self):
00361 return True
00362
00363 class Literal(Condition):
00364 parts = []
00365 def __eq__(self, other):
00366
00367 return (self.hash == other.hash and
00368 self.__class__ is other.__class__ and
00369 self.predicate == other.predicate and
00370 self.args == other.args)
00371 def __init__(self, predicate, args):
00372 self.predicate = predicate
00373 self.args = tuple(args)
00374 self.hash = hash((self.__class__, self.predicate, self.args))
00375 def __str__(self):
00376 return "%s %s(%s)" % (self.__class__.__name__, self.predicate,
00377 ", ".join(map(str, self.args)))
00378 def dump(self, indent=" "):
00379 print "%s%s %s" % (indent, self._dump(), self.predicate)
00380 for arg in self.args:
00381 arg.dump(indent + " ")
00382 def change_parts(self, parts):
00383 return self
00384 def uniquify_variables(self, type_map, renamings={}):
00385 if not self.args:
00386 return self
00387 else:
00388 return self.__class__(self.predicate,[arg.uniquify_variables(type_map, renamings)
00389 for arg in self.args])
00390 def rename_variables(self, renamings):
00391 new_args = [arg.rename_variables(renamings) for arg in self.args]
00392 return self.__class__(self.predicate, new_args)
00393 def free_variables(self):
00394 result = set()
00395 for arg in self.args:
00396 result |= arg.free_variables()
00397 return result
00398
00399 class Atom(Literal):
00400 negated = False
00401 def to_untyped_strips(self):
00402 return [self]
00403 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00404 fluent_functions, task, new_axiom, new_modules, result):
00405 args = [var_mapping.get(arg, arg) for arg in self.args]
00406 atom = Atom(self.predicate, args)
00407 if atom in fluent_facts:
00408 result.append(atom)
00409 elif atom not in init_facts:
00410 raise Impossible()
00411 def negate(self):
00412 return NegatedAtom(self.predicate, self.args)
00413 def positive(self):
00414 return self
00415
00416 class NegatedAtom(Literal):
00417 negated = True
00418 def _relaxed(self, parts):
00419 return Truth()
00420 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00421 fluent_functions, task, new_axiom, new_modules, result):
00422 args = [var_mapping.get(arg, arg) for arg in self.args]
00423 atom = Atom(self.predicate, args)
00424 if atom in fluent_facts:
00425 result.append(NegatedAtom(self.predicate, args))
00426 elif atom in init_facts:
00427 raise Impossible()
00428 def negate(self):
00429 return Atom(self.predicate, self.args)
00430 positive = negate
00431
00432 class ModuleCall(Condition):
00433
00434 negated = False
00435 parts = []
00436 def __eq__(self, other):
00437
00438 return (self.hash == other.hash and
00439 self.__class__ is other.__class__ and
00440 self.name == other.name and
00441 self.args == other.args)
00442 def __init__(self, name, args):
00443 self.name = name
00444 self.args = tuple(args)
00445 self.hash = hash((self.__class__, self.name, self.args))
00446 def change_parts(self, parts):
00447 return self
00448 def rename_variables(self, renamings):
00449 new_args = [arg.rename_variables(renamings) for arg in self.args]
00450 return self.__class__(self.name, new_args)
00451 def free_variables(self):
00452 result = set()
00453 for arg in self.args:
00454 result |= arg.free_variables()
00455 return result
00456 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00457 fluent_functions, task, new_axiom, new_modules, result):
00458 args = [var_mapping.get(arg, arg) for arg in self.args]
00459 mc = ModuleCall(self.name, args)
00460 result.append(mc)
00461
00462 my_module = None
00463 for module in task.modules:
00464 if module.name == self.name:
00465 my_module = module
00466 break
00467 assert my_module, "No matching module for call found"
00468
00469 if my_module:
00470
00471
00472
00473
00474
00475 assert len(my_module.parameters) == len(self.args)
00476 renamings = {}
00477 for param, arg in zip(my_module.parameters, self.args):
00478 pVar = Variable(param.name)
00479 renamings[pVar] = arg
00480 new_module = my_module.rename_variables(renamings)
00481 new_module.instantiate(var_mapping, new_modules)
00482 def __str__(self):
00483 return "%s %s(%s)" % (self.__class__.__name__, self.name,
00484 ", ".join(map(str, self.args)))
00485 def __repr__(self):
00486 return self.__str__()
00487 def dump(self, indent=" "):
00488 print "%s%s %s" % (indent, self._dump(), self.name)
00489 for arg in self.args:
00490 arg.dump(indent + " ")
00491
00492 class FunctionComparison(Condition):
00493 negated = False
00494 def _relaxed(self, parts):
00495 return Truth()
00496 def __init__(self, comparator, parts, compare_to_zero = False):
00497 self.comparator = comparator
00498 assert len(parts) == 2
00499 if compare_to_zero:
00500 self.parts = (f_expression.Difference(parts), f_expression.NumericConstant(0))
00501 else:
00502 self.parts = tuple(parts)
00503 self.hash = hash((self.__class__, self.comparator, self.parts))
00504 def _dump(self, indent=" "):
00505 return "%s %s" % (self.__class__.__name__, self.comparator)
00506 def __eq__(self, other):
00507
00508 return (self.hash == other.hash and
00509 self.__class__ is other.__class__ and
00510 self.comparator == other.comparator and
00511 self.parts == other.parts)
00512 def __str__(self):
00513 return "%s (%s %s)" % (self.__class__.__name__, self.comparator,
00514 ", ".join(map(str, self.parts)))
00515 def uniquify_variables(self, type_map, renamings={}):
00516 return self.__class__(self.comparator,[part.rename_variables(renamings)
00517 for part in self.parts])
00518 def has_disjunction(self):
00519 return False
00520 def has_universal_part(self):
00521 return False
00522 def has_existential_part(self):
00523 return False
00524 def negate(self):
00525 return NegatedFunctionComparison(self.comparator, self.parts)
00526 def change_parts(self, parts):
00527 return self.__class__(self.comparator,parts)
00528 def primitive_numeric_expressions(self):
00529 result = set()
00530 for part in self.parts:
00531 result |= part.primitive_numeric_expressions()
00532 return result
00533 def instantiate(self, var_mapping, init_facts, fluent_facts, init_function_vals,
00534 fluent_functions, task, new_axiom, new_modules, result):
00535 instantiated_parts = [part.instantiate(var_mapping, fluent_functions,
00536 init_function_vals, task, new_axiom)
00537 for part in self.parts]
00538
00539 result.append(self.__class__(self.comparator,instantiated_parts))
00540 def positive(self):
00541 return self
00542
00543
00544 class NegatedFunctionComparison(FunctionComparison):
00545 negated = True
00546 def negate(self):
00547 return FunctionComparison(self.comparator, self.parts)
00548 positive = negate
00549
00550 class Term(object):
00551 def dump(self, indent=" "):
00552 print "%s%s %s" % (indent, self._dump(), self.name)
00553 for arg in self.args:
00554 arg.dump(indent + " ")
00555 def _dump(self):
00556 return self.__class__.__name__
00557 def __eq__(self, other):
00558 return (self.__class__ is other.__class__ and
00559 self.args == other.args)
00560 def uniquify_variables(self, type_map, renamings={}):
00561 if not self.args:
00562 return self
00563 else:
00564 return self.__class__(self.name,[arg.uniquify_variables(type_map, renamings)
00565 for arg in self.args])
00566 def compile_objectfunctions_aux(self, used_variables, recurse_object_terms=True):
00567 return ([],[],self)
00568 def rename_variables(self, renamings):
00569 new_args = [renamings.get(arg, arg) for arg in self.args]
00570 return self.__class__(self.name, new_args)
00571 def free_variables(self):
00572 result = set()
00573 for arg in self.args:
00574 result |= arg.free_variables()
00575 return result
00576
00577 class FunctionTerm(Term):
00578 def __init__(self, name, args=[]):
00579 self.name = name
00580 self.args = args
00581 def __str__(self):
00582 return "%s(%s)" % (self.name, ", ".join(map(str, self.args)))
00583 def compile_objectfunctions_aux(self, used_variables, recurse_object_terms=True):
00584
00585 typed_vars = []
00586 conjunction_parts = []
00587 new_args = []
00588 for arg in self.args:
00589 if recurse_object_terms:
00590 typed,parts,new_term = arg.compile_objectfunctions_aux(used_variables)
00591 typed_vars += typed
00592 conjunction_parts += parts
00593 new_args.append(new_term)
00594
00595 for counter in itertools.count(1):
00596 new_var_name = "?v" + str(counter)
00597 if new_var_name not in used_variables:
00598 used_variables.append(new_var_name)
00599 typed_vars.append(pddl_types.TypedObject(new_var_name, tasks.Task.FUNCTION_SYMBOLS[self.name]))
00600 new_var = Variable(new_var_name)
00601 break
00602
00603 if recurse_object_terms:
00604 pred_name = function_predicate_name(self.name)
00605 new_args.append(new_var)
00606 atom = Atom(pred_name,new_args)
00607 conjunction_parts = [atom] + conjunction_parts
00608 else:
00609 conjunction_parts = [self]
00610 return (typed_vars, conjunction_parts, new_var)
00611
00612 class Variable(Term):
00613 args = []
00614 def __init__(self, name):
00615 self.name = name
00616 self.hash = hash((self.__class__,self.name))
00617 def __eq__(self, other):
00618 return (self.__class__ is other.__class__ and
00619 self.name == other.name)
00620 def __cmp__(self,other):
00621 return cmp(self.name,other.name)
00622 def __hash__(self):
00623 return self.hash
00624 def __str__(self):
00625 return "<%s>" % self.name
00626 def uniquify_variables(self, type_map, renamings={}):
00627 return self.rename_variables(renamings)
00628 def rename_variables(self, renamings):
00629 return self.__class__(renamings.get(self.name,self.name))
00630 def free_variables(self):
00631 return set((self.name,))
00632
00633 class ObjectTerm(Term):
00634 args = []
00635 def __init__(self, name):
00636 self.name = name
00637 self.hash = hash((self.__class__,self.name))
00638 def __eq__(self, other):
00639 return (self.__class__ is other.__class__ and
00640 self.name == other.name)
00641 def __cmp__(self,other):
00642 return cmp(self.name,other.name)
00643 def __str__(self):
00644 return "<%s>" % self.name
00645 def __hash__(self):
00646 return self.hash
00647 def free_variables(self):
00648 return set()
00649 def rename_variables(self, renamings):
00650 return self
00651
00652 def function_predicate_name(functionname):
00653 return "%s!val" % functionname
00654