예제 #1
0
파일: action.py 프로젝트: aiplan4eu/upf
 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))
예제 #2
0
 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)
예제 #3
0
 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)
예제 #4
0
 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
예제 #5
0
    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
예제 #6
0
 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
예제 #7
0
 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
예제 #8
0
 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
예제 #9
0
 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)
예제 #10
0
    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
예제 #11
0
    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)
예제 #12
0
    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')
예제 #13
0
 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