def add_effect(self, timing: 'up.model.timing.Timing', fluent: Union['up.model.fnode.FNode', 'up.model.fluent.Fluent'], value: 'up.model.expression.Expression', condition: 'up.model.expression.BoolExpression' = True): '''Adds the given action effect.''' fluent_exp, value_exp, condition_exp = self._env.expression_manager.auto_promote(fluent, value, condition) assert fluent_exp.is_fluent_exp() if not self._env.type_checker.get_type(condition_exp).is_bool_type(): raise UPTypeError('Effect condition is not a Boolean condition!') if not self._env.type_checker.is_compatible_exp(fluent_exp, value_exp): raise UPTypeError('InstantaneousAction effect has not compatible types!') self._add_effect_instance(timing, up.model.effect.Effect(fluent_exp, value_exp, condition_exp))
def Real(self, value: Fraction) -> 'unified_planning.model.fnode.FNode': """Return a real constant.""" if type(value) != Fraction: raise UPTypeError("Expecting Fraction, got %s" % type(value)) return self.create_node(node_type=OperatorKind.REAL_CONSTANT, args=tuple(), payload=value)
def Int(self, value: int) -> 'unified_planning.model.fnode.FNode': """Return an int constant.""" if type(value) != int: raise UPTypeError("Expecting int, got %s" % type(value)) return self.create_node(node_type=OperatorKind.INT_CONSTANT, args=tuple(), payload=value)
def get_type(self, expression: FNode) -> 'unified_planning.model.types.Type': """ Returns the unified_planning.model.types type of the expression """ res = self.walk(expression) if res is None: raise UPTypeError("The expression '%s' is not well-formed" \ % str(expression)) return res
def Bool(self, value: bool) -> 'unified_planning.model.fnode.FNode': """Return a boolean constant.""" if type(value) != bool: raise UPTypeError("Expecting bool, got %s" % type(value)) if value: return self.true_expression else: return self.false_expression
def user_type_ancestors(self, user_type: Type) -> Iterator[Type]: '''Returns all the ancestors of the given UserType, including itself.''' if not user_type.is_user_type(): raise UPTypeError( 'The function user_type_ancestors can be called only on UserTypes.' ) yield user_type father: Optional[Type] = user_type.father # type: ignore while father is not None: yield father father = father.father # type: ignore
def UserType(self, name: str, father: Optional[Type] = None) -> Type: if (name, father) in self._user_types: return self._user_types[(name, father)] else: if father is not None: if any(ancestor.name == name for ancestor in self.user_type_ancestors(father)): # type: ignore raise UPTypeError( f'The name: {name} is already used. A UserType and one of his ancestors can not share the name.' ) ut = _UserType(name, father) self._user_types[(name, father)] = ut return ut
def set_initial_value(self, fluent: Union['up.model.fnode.FNode', 'up.model.fluent.Fluent'], value: Union['up.model.fnode.FNode', 'up.model.fluent.Fluent', 'up.model.object.Object', bool, int, float, Fraction]): '''Sets the initial value for the given fluent.''' fluent_exp, value_exp = self._env.expression_manager.auto_promote( fluent, value) if not self._env.type_checker.is_compatible_exp(fluent_exp, value_exp): raise UPTypeError( 'Initial value assignment has not compatible types!') self._initial_value[fluent_exp] = value_exp
def Forall( self, expression: BoolExpression, *vars: 'unified_planning.model.variable.Variable' ) -> 'unified_planning.model.fnode.FNode': """ Creates an expression of the form: Forall (var[0]... var[n]) | expression Restriction: expression must be of boolean type and vars must be of 'unified_planning.Variable' type """ expressions = tuple(self.auto_promote(expression)) if len(vars) == 0: raise UPExpressionDefinitionError( f"Forall of expression: {str(expression)} must be created with at least one variable, otherwise it is not needed." ) for v in vars: if not isinstance(v, unified_planning.model.variable.Variable): raise UPTypeError( "Expecting 'unified_planning.Variable', got %s", type(v)) return self.create_node(node_type=OperatorKind.FORALL, args=expressions, payload=vars)
def walk_equals( self, expression: FNode, args: List['unified_planning.model.types.Type'] ) -> Optional['unified_planning.model.types.Type']: t = args[0] if t is None: return None if t.is_bool_type(): raise UPTypeError("The expression '%s' is not well-formed." "Equality operator is not supported for Boolean" " terms. Use Iff instead." \ % str(expression)) for x in args: if x is None: return None elif t.is_user_type() and t != x: return None elif (t.is_int_type() or t.is_real_type() ) and not (x.is_int_type() or x.is_real_type()): return None return BOOL
def substitute(self, expression: FNode, substitutions: Dict[Expression, Expression] = {}) -> FNode: """Performs substitution into the given expression. Lets consider the examples: f = a & b subs = {a -> c, (c & b) -> d, (a & b) -> c} substitute(f, subs) = c f = a subs = {a -> c, c -> d} substitute(f, subs) = c f = a & b subs = {a -> 5, b -> c} substitute(f, subs) raises an UPTypeError Note that, since subs is a dictionary: f = a subs = {a -> b, a -> c} substitute(f, subs) = c """ if len(substitutions) == 0: return expression new_substitutions: Dict[FNode, FNode] = {} for k, v in substitutions.items(): new_k, new_v = self.manager.auto_promote(k, v) if self.type_checker.is_compatible_exp(new_v, new_k): new_substitutions[new_k] = new_v else: raise UPTypeError( f"The expression type of {str(k)} is not compatible with the given substitution {str(v)}" ) return self.walk(expression, subs=new_substitutions)
def _write_domain(self, out: IO[str]): problem_kind = self.problem.kind if problem_kind.has_intermediate_conditions_and_effects( ): # type: ignore raise UPProblemDefinitionError( 'PDDL2.1 does not support ICE.\nICE are Intermediate Conditions and Effects therefore when an Effect (or Condition) are not at StartTIming(0) or EndTIming(0).' ) if problem_kind.has_timed_effect() or problem_kind.has_timed_goals( ): # type: ignore raise UPProblemDefinitionError( 'PDDL2.1 does not support timed effects or timed goals.') out.write('(define ') if self.problem.name is None: name = 'pddl' else: name = f'{self.problem.name}' out.write(f'(domain {name}-domain)\n') if self.needs_requirements: out.write(' (:requirements :strips') if problem_kind.has_flat_typing(): # type: ignore out.write(' :typing') if problem_kind.has_negative_conditions(): # type: ignore out.write(' :negative-preconditions') if problem_kind.has_disjunctive_conditions(): # type: ignore out.write(' :disjunctive-preconditions') if problem_kind.has_equality(): # type: ignore out.write(' :equality') if (problem_kind.has_continuous_numbers() or # type: ignore problem_kind.has_discrete_numbers()): # type: ignore out.write(' :numeric-fluents') if problem_kind.has_conditional_effects(): # type: ignore out.write(' :conditional-effects') if problem_kind.has_existential_conditions(): # type: ignore out.write(' :existential-preconditions') if problem_kind.has_universal_conditions(): # type: ignore out.write(' :universal-preconditions') if (problem_kind.has_continuous_time() or # type: ignore problem_kind.has_discrete_time()): # type: ignore out.write(' :durative-actions') if problem_kind.has_duration_inequalities(): # type: ignore out.write(' :duration-inequalities') if (self.problem.kind.has_actions_cost() or # type: ignore self.problem.kind.has_plan_length()): # type: ignore out.write(' :action-costs') out.write(')\n') if problem_kind.has_hierarchical_typing(): # type: ignore while self.problem.has_type(self.object_freshname): self.object_freshname = self.object_freshname + '_' user_types_hierarchy = self.problem.user_types_hierarchy out.write(f' (:types\n') stack: List['unified_planning.model.Type'] = user_types_hierarchy[ None] if None in user_types_hierarchy else [] out.write( f' {" ".join(self._type_name_or_object_freshname(t) for t in stack)} - object\n' ) while stack: current_type = stack.pop() direct_sons: List[ 'unified_planning.model.Type'] = user_types_hierarchy[ current_type] if direct_sons: stack.extend(direct_sons) out.write( f' {" ".join([self._type_name_or_object_freshname(t) for t in direct_sons])} - {self._type_name_or_object_freshname(current_type)}\n' ) out.write(' )\n') else: out.write( f' (:types {" ".join([t.name for t in self.problem.user_types])})\n' if len(self.problem.user_types) > 0 else '') # type: ignore predicates = [] functions = [] for f in self.problem.fluents: if f.type.is_bool_type(): params = [] i = 0 for param in f.signature: if param.type.is_user_type(): params.append( f' ?{param.name} - {self._type_name_or_object_freshname(param.type)}' ) i += 1 else: raise UPTypeError( 'PDDL supports only user type parameters') predicates.append(f'({f.name}{"".join(params)})') elif f.type.is_int_type() or f.type.is_real_type(): params = [] i = 0 for param in f.signature: if param.type.is_user_type(): params.append( f' ?{param.name} - {self._type_name_or_object_freshname(param.type)}' ) i += 1 else: raise UPTypeError( 'PDDL supports only user type parameters') functions.append(f'({f.name}{"".join(params)})') else: raise UPTypeError( 'PDDL supports only boolean and numerical fluents') if (self.problem.kind.has_actions_cost() or # type: ignore self.problem.kind.has_plan_length()): # type: ignore functions.append('(total-cost)') out.write(f' (:predicates {" ".join(predicates)})\n' if len(predicates) > 0 else '') out.write(f' (:functions {" ".join(functions)})\n' if len(functions) > 0 else '') converter = ConverterToPDDLString(self.problem.env) costs = {} metrics = self.problem.quality_metrics if len(metrics) == 1: metric = metrics[0] if isinstance(metric, up.model.metrics.MinimizeActionCosts): for a in self.problem.actions: costs[a] = metric.get_action_cost(a) elif isinstance(metric, up.model.metrics.MinimizeSequentialPlanLength): for a in self.problem.actions: costs[a] = self.problem.env.expression_manager.Int(1) elif len(metrics) > 1: raise up.exceptions.UPUnsupportedProblemTypeError( 'Only one metric is supported!') for a in self.problem.actions: if isinstance(a, up.model.InstantaneousAction): out.write(f' (:action {a.name}') out.write(f'\n :parameters (') for ap in a.parameters: if ap.type.is_user_type(): out.write( f' ?{ap.name} - {self._type_name_or_object_freshname(ap.type)}' ) else: raise UPTypeError( 'PDDL supports only user type parameters') out.write(')') if len(a.preconditions) > 0: out.write( f'\n :precondition (and {" ".join([converter.convert(p) for p in a.preconditions])})' ) if len(a.effects) > 0: out.write('\n :effect (and') for e in a.effects: if e.is_conditional(): out.write( f' (when {converter.convert(e.condition)}') if e.value.is_true(): out.write(f' {converter.convert(e.fluent)}') elif e.value.is_false(): out.write(f' (not {converter.convert(e.fluent)})') elif e.is_increase(): out.write( f' (increase {converter.convert(e.fluent)} {converter.convert(e.value)})' ) elif e.is_decrease(): out.write( f' (decrease {converter.convert(e.fluent)} {converter.convert(e.value)})' ) else: out.write( f' (assign {converter.convert(e.fluent)} {converter.convert(e.value)})' ) if e.is_conditional(): out.write(f')') if a in costs: out.write( f' (increase total-cost {converter.convert(costs[a])})' ) out.write(')') out.write(')\n') elif isinstance(a, DurativeAction): out.write(f' (:durative-action {a.name}') out.write(f'\n :parameters (') for ap in a.parameters: if ap.type.is_user_type(): out.write( f' ?{ap.name} - {self._type_name_or_object_freshname(ap.type)}' ) else: raise UPTypeError( 'PDDL supports only user type parameters') out.write(')') l, r = a.duration.lower, a.duration.upper if l == r: out.write(f'\n :duration (= ?duration {str(l)})') else: out.write(f'\n :duration (and ') if a.duration.is_left_open(): out.write(f'(> ?duration {str(l)})') else: out.write(f'(>= ?duration {str(l)})') if a.duration.is_right_open(): out.write(f'(< ?duration {str(r)})') else: out.write(f'(<= ?duration {str(r)})') out.write(')') if len(a.conditions) > 0: out.write(f'\n :condition (and ') for interval, cl in a.conditions.items(): for c in cl: if interval.lower == interval.upper: if interval.lower.is_from_start(): out.write( f'(at start {converter.convert(c)})') else: out.write( f'(at end {converter.convert(c)})') else: if not interval.is_left_open(): out.write( f'(at start {converter.convert(c)})') out.write(f'(over all {converter.convert(c)})') if not interval.is_right_open(): out.write( f'(at end {converter.convert(c)})') out.write(')') if len(a.effects) > 0: out.write('\n :effect (and') for t, el in a.effects.items(): for e in el: if t.is_from_start(): out.write(f' (at start') else: out.write(f' (at end') if e.is_conditional(): out.write( f' (when {converter.convert(e.condition)}') if e.value.is_true(): out.write(f' {converter.convert(e.fluent)}') elif e.value.is_false(): out.write( f' (not {converter.convert(e.fluent)})') elif e.is_increase(): out.write( f' (increase {converter.convert(e.fluent)} {converter.convert(e.value)})' ) elif e.is_decrease(): out.write( f' (decrease {converter.convert(e.fluent)} {converter.convert(e.value)})' ) else: out.write( f' (assign {converter.convert(e.fluent)} {converter.convert(e.value)})' ) if e.is_conditional(): out.write(f')') out.write(')') if a in costs: out.write( f' (at end (increase total-cost {converter.convert(costs[a])}))' ) out.write(')') out.write(')\n') else: raise NotImplementedError out.write(')\n')
def __init__(self, name: str, father: Optional[Type] = None): Type.__init__(self) self._name = name if father is not None and (not father.is_user_type()): raise UPTypeError('father field of a UserType must be a UserType.') self._father = father