Beispiel #1
0
 def set_duration_constraint(self, duration: 'up.model.timing.DurationInterval'):
     '''Sets the duration interval.'''
     lower, upper = duration.lower, duration.upper
     if not (lower.is_int_constant() or lower.is_real_constant()):
         raise UPProblemDefinitionError('Duration bound must be constant.')
     elif not (upper.is_int_constant() or upper.is_real_constant()):
         raise UPProblemDefinitionError('Duration bound must be constant.')
     elif (upper.constant_value() < lower.constant_value() or
           (upper.constant_value() == lower.constant_value() and
            (duration.is_left_open() or duration.is_right_open()))):
         raise UPProblemDefinitionError(f'{duration} is an empty interval duration of action: {self.name}.')
     self._duration = duration
Beispiel #2
0
def domain_size(problem: 'unified_planning.model.problem.Problem',
                typename: 'unified_planning.model.types.Type') -> int:
    '''Returns the domain size of the given type.'''
    if typename.is_bool_type():
        return 2
    elif typename.is_user_type():
        return len(list(problem.objects_hierarchy(typename)))
    elif typename.is_int_type():
        lb = typename.lower_bound  # type: ignore
        ub = typename.upper_bound  # type: ignore
        if lb is None or ub is None:
            raise UPProblemDefinitionError('Parameter not groundable!')
        return ub - lb
    else:
        raise UPProblemDefinitionError('Parameter not groundable!')
Beispiel #3
0
    def add_object(
        self,
        obj_or_name: Union['up.model.object.Object', str],
        typename: Optional['up.model.types.Type'] = None
    ) -> 'up.model.object.Object':
        """Add the given object to the problem, constructing it from the parameters if needed.

        :param obj_or_name: Either an Object instance or a string containing the name of the object.
        :param typename: If the first argument contains only the name of the object, this parameter should contain
                         its type, to allow creating the object.
        :return: The Object that was passed or constructed.

        Examples
        --------
        >>> from unified_planning.shortcuts import *
        >>> problem = Problem()
        >>> cup = UserType("Cup")
        >>> o1 = Object("o1", cup)  # creates a new object o1
        >>> problem.add_object(o1)  # adds it to the problem
        o1
        >>> o2 = problem.add_object("o2", cup)  # alternative syntax to create a new object and add it to the problem.
        """
        if isinstance(obj_or_name, up.model.object.Object):
            assert typename is None
            obj = obj_or_name
        else:
            assert typename is not None, "Missing type of the object"
            obj = up.model.object.Object(obj_or_name, typename)
        if self.has_name(obj.name):
            raise UPProblemDefinitionError('Name ' + obj.name +
                                           ' already defined!')
        self._objects.append(obj)
        if obj.type.is_user_type() and obj.type not in self._user_types:
            self._add_user_type(obj.type)
        return obj
Beispiel #4
0
    def add_fluent(
            self,
            fluent_or_name: Union['up.model.fluent.Fluent', str],
            typename: 'up.model.types.Type' = None,
            *,
            default_initial_value: Union['up.model.fnode.FNode',
                                         'up.model.object.Object', bool, int,
                                         float, Fraction] = None,
            **kwargs: 'up.model.types.Type') -> 'up.model.fluent.Fluent':
        """Adds the given fluent to the problem.

        If the first parameter is not a Fluent, the parameters will be passed to the Fluent constructor to create it.

        :param fluent_or_name: Fluent instance or name of the fluent to be constructed
        :param typename: If only the name of the fluent is given, this is the fluent's type (passed to the Fluent constructor).
        :param default_initial_value: If provided, defines the default value taken in initial state by
                                      a state variable of this fluent that has no explicit value.
        :param kwargs: If only the name of the fluent is given, these are the fluent's parameters (passed to the Fluent constructor).
        :return: The fluent passed or constructed.

        Example
        --------
        >>> from unified_planning.shortcuts import *
        >>> problem = Problem()
        >>> location = UserType("Location")
        >>> at_loc = Fluent("at_loc", BoolType(), l=location)  # creates a new fluent
        >>> problem.add_fluent(at_loc)  # adds it to the problem
        bool at_loc[l=Location]
        >>> problem.add_fluent("connected", BoolType(), l1=location, l2=location)  # creates a new fluent and add it to the problem.
        bool connected[l1=Location, l2=Location]
        >>>
        """
        if isinstance(fluent_or_name, up.model.fluent.Fluent):
            assert len(kwargs) == 0 and typename is None
            fluent = fluent_or_name
        else:
            fluent = up.model.fluent.Fluent(fluent_or_name,
                                            typename,
                                            None,
                                            env=self.env,
                                            **kwargs)
        if self.has_name(fluent.name):
            raise UPProblemDefinitionError('Name ' + fluent.name +
                                           ' already defined!')
        self._fluents.append(fluent)
        if not default_initial_value is None:
            v_exp, = self._env.expression_manager.auto_promote(
                default_initial_value)
            self._fluents_defaults[fluent] = v_exp
        elif fluent.type in self._initial_defaults:
            self._fluents_defaults[fluent] = self._initial_defaults[
                fluent.type]
        if fluent.type.is_user_type() and fluent.type not in self._user_types:
            self._add_user_type(fluent.type)
        for param in fluent.signature:
            if param.type.is_user_type(
            ) and param.type not in self._user_types:
                self._add_user_type(param.type)
        return fluent
Beispiel #5
0
def domain_item(problem: 'unified_planning.model.problem.Problem',
                typename: 'unified_planning.model.types.Type',
                idx: int) -> 'unified_planning.model.fnode.FNode':
    '''Returns the ith domain item of the given type.'''
    if typename.is_bool_type():
        return problem._env.expression_manager.Bool(idx == 0)
    elif typename.is_user_type():
        return problem._env.expression_manager.ObjectExp(
            list(problem.objects_hierarchy(typename))[idx])
    elif typename.is_int_type():
        lb = typename.lower_bound  # type: ignore
        ub = typename.upper_bound  # type: ignore
        if lb is None or ub is None:
            raise UPProblemDefinitionError('Parameter not groundable!')
        return problem._env.expression_manager.Int(lb + idx)
    else:
        raise UPProblemDefinitionError('Parameter not groundable!')
Beispiel #6
0
 def walk_param_exp(self, expression: FNode, args: List[FNode]) -> FNode:
     assert self._assignments is not None
     res = self._assignments.get(expression.parameter(), None)
     if res is not None:
         res, = self.manager.auto_promote(res)
         assert type(res) is FNode
         return res
     else:
         raise UPProblemDefinitionError(
             f"Value of Parameter {str(expression)} not found in {str(self._assignments)}"
         )
Beispiel #7
0
 def walk_fluent_exp(self, expression: FNode, args: List[FNode]) -> FNode:
     new_exp = self.manager.FluentExp(expression.fluent(), tuple(args))
     assert self._assignments is not None
     res = self._assignments.get(new_exp, None)
     if res is not None:
         res, = self.manager.auto_promote(res)
         assert type(res) is FNode
         return res
     else:
         raise UPProblemDefinitionError(
             f"Value of Fluent {str(expression)} not found in {str(self._assignments)}"
         )
Beispiel #8
0
 def _add_user_type(self, type: Optional['up.model.types.Type']):
     '''This method adds a Type, together with all it's ancestors, to the user_types_hierarchy'''
     assert (type is None) or type.is_user_type()
     if type not in self._user_types_hierarchy:
         if type is not None:
             if any(ut.name == type.name
                    for ut in self._user_types):  # type: ignore
                 raise UPProblemDefinitionError(
                     f'The type {type} is already used in the problem')
             self._add_user_type(type.father)  # type: ignore
             self._user_types_hierarchy[type.father].append(
                 type)  # type: ignore
             self._user_types.append(type)
         self._user_types_hierarchy[type] = []
Beispiel #9
0
 def initial_value(
     self, fluent: Union['up.model.fnode.FNode', 'up.model.fluent.Fluent']
 ) -> 'up.model.fnode.FNode':
     '''Gets the initial value of the given fluent.'''
     fluent_exp, = self._env.expression_manager.auto_promote(fluent)
     for a in fluent_exp.args:
         if not a.is_constant():
             raise UPExpressionDefinitionError(
                 f'Impossible to return the initial value of a fluent expression with no constant arguments: {fluent_exp}.'
             )
     if fluent_exp in self._initial_value:
         return self._initial_value[fluent_exp]
     elif fluent_exp.fluent() in self._fluents_defaults:
         return self._fluents_defaults[fluent_exp.fluent()]
     else:
         print(fluent)
         raise UPProblemDefinitionError('Initial value not set!')
Beispiel #10
0
 def add_timed_goal(self, interval: Union['up.model.timing.Timing',
                                          'up.model.timing.TimeInterval'],
                    goal: Union['up.model.fnode.FNode',
                                'up.model.fluent.Fluent', bool]):
     '''Adds a timed goal.'''
     if isinstance(interval, up.model.Timing):
         interval = up.model.TimePointInterval(interval)
     if ((interval.lower.is_from_end() and interval.lower.delay > 0) or
         (interval.upper.is_from_end() and interval.upper.delay > 0)):
         raise UPProblemDefinitionError(
             'Problem timing can not be `end - k` with k > 0.')
     goal_exp, = self._env.expression_manager.auto_promote(goal)
     assert self._env.type_checker.get_type(goal_exp).is_bool_type()
     if interval in self._timed_goals:
         if goal_exp not in self._timed_goals[interval]:
             self._timed_goals[interval].append(goal_exp)
     else:
         self._timed_goals[interval] = [goal_exp]
Beispiel #11
0
 def add_timed_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 timed effect.'''
     if timing.is_from_end():
         raise UPProblemDefinitionError(
             f'Timing used in timed effect cannot be EndTiming.')
     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('Timed effect has not compatible types!')
     self._add_effect_instance(
         timing, up.model.effect.Effect(fluent_exp, value_exp,
                                        condition_exp))
Beispiel #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')
 def get_rewritten_problem(self) -> 'unified_planning.model.Problem':
     '''Creates a problem that is a copy of the original problem
     but every conditional action or effect are removed.'''
     if self._new_problem is not None:
         return self._new_problem
     #NOTE that a different environment might be needed when multi-threading
     self._new_problem = self._problem.clone()
     self._new_problem.name = f'{self._name}_{self._problem.name}'
     self._new_problem.clear_timed_effects()
     for t, el in self._problem.timed_effects.items():
         for e in el:
             if e.is_conditional():
                 f, v = e.fluent.fluent(), e.value
                 if not f.type.is_bool_type():
                     raise UPProblemDefinitionError(
                         f'The condition of effect: {e}\ncould not be removed without changing the problem.'
                     )
                 else:
                     em = self._env.expression_manager
                     c = e.condition
                     nv = self._simplifier.simplify(
                         em.Or(em.And(c, v), em.And(em.Not(c), f)))
                     self._new_problem.add_timed_effect(t, e.fluent, nv)
             else:
                 self._new_problem._add_effect_instance(t, e.clone())
     self._new_problem.clear_actions()
     for ua in self._problem.unconditional_actions:
         new_uncond_action = ua.clone()
         self._new_problem.add_action(new_uncond_action)
         self._new_to_old[new_uncond_action] = ua
         self._map_old_to_new_action(ua, new_uncond_action)
     for action in self._problem.conditional_actions:
         if isinstance(action, unified_planning.model.InstantaneousAction):
             cond_effects = action.conditional_effects
             for p in self.powerset(range(len(cond_effects))):
                 new_action = action.clone()
                 new_action.name = self.get_fresh_name(action.name)
                 new_action.clear_effects()
                 for e in action.unconditional_effects:
                     new_action._add_effect_instance(e.clone())
                 for i, e in enumerate(cond_effects):
                     if i in p:
                         # positive precondition
                         new_action.add_precondition(e.condition)
                         ne = unified_planning.model.Effect(
                             e.fluent, e.value,
                             self._env.expression_manager.TRUE(), e.kind)
                         new_action._add_effect_instance(ne)
                     else:
                         #negative precondition
                         new_action.add_precondition(
                             self._env.expression_manager.Not(e.condition))
                 #new action is created, then is checked if it has any impact and if it can be simplified
                 if len(new_action.effects) > 0:
                     action_is_feasible, simplified_preconditions = self._check_and_simplify_preconditions(
                         new_action)
                     if action_is_feasible:
                         new_action._set_preconditions(
                             simplified_preconditions)
                         self._new_to_old[new_action] = action
                         self._map_old_to_new_action(action, new_action)
                         self._new_problem.add_action(new_action)
         elif isinstance(action, unified_planning.model.DurativeAction):
             timing_cond_effects: Dict['unified_planning.model.Timing', List[
                 'unified_planning.model.Effect']] = action.conditional_effects
             cond_effects_timing: List[
                 Tuple['unified_planning.model.Effect',
                       'unified_planning.model.Timing']] = [
                           (e, t) for t, el in timing_cond_effects.items()
                           for e in el
                       ]
             for p in self.powerset(range(len(cond_effects_timing))):
                 new_action = action.clone()
                 new_action.name = self.get_fresh_name(action.name)
                 new_action.clear_effects()
                 for t, el in action.unconditional_effects.items():
                     for e in el:
                         new_action._add_effect_instance(t, e.clone())
                 for i, (e, t) in enumerate(cond_effects_timing):
                     if i in p:
                         # positive precondition
                         new_action.add_condition(t, e.condition)
                         ne = unified_planning.model.Effect(
                             e.fluent, e.value,
                             self._env.expression_manager.TRUE(), e.kind)
                         new_action._add_effect_instance(t, ne)
                     else:
                         #negative precondition
                         new_action.add_condition(
                             t,
                             self._env.expression_manager.Not(e.condition))
                 #new action is created, then is checked if it has any impact and if it can be simplified
                 if len(new_action.effects) > 0:
                     action_is_feasible, simplified_conditions = self._check_and_simplify_conditions(
                         new_action)
                     if action_is_feasible:
                         new_action.clear_conditions()
                         for interval, c in simplified_conditions:
                             new_action.add_condition(interval, c)
                         self._new_to_old[new_action] = action
                         self._map_old_to_new_action(action, new_action)
                         self._new_problem.add_action(new_action)
         else:
             raise NotImplementedError
     return self._new_problem
Beispiel #14
0
def convert_tarski_formula(
        env: Environment, fluents: Dict[str, 'unified_planning.model.Fluent'],
        objects: Dict[str, 'unified_planning.model.Object'],
        action_parameters: Dict[str, 'unified_planning.model.Parameter'],
        types: Dict[str, Optional['unified_planning.model.Type']],
        formula: Union[Formula, Term]) -> 'unified_planning.model.FNode':
    """Converts a tarski formula in a unified_planning expression."""
    em = env.expression_manager
    if is_and(formula):
        children = [
            convert_tarski_formula(env, fluents, objects, action_parameters,
                                   types, f) for f in formula.subformulas
        ]
        return em.And(*children)
    elif is_or(formula):
        children = [
            convert_tarski_formula(env, fluents, objects, action_parameters,
                                   types, f) for f in formula.subformulas
        ]
        return em.Or(*children)
    elif is_neg(formula):
        assert len(formula.subformulas) == 1
        return em.Not(
            convert_tarski_formula(env, fluents, objects, action_parameters,
                                   types, formula.subformulas[0]))
    elif is_atom(formula) or isinstance(formula, CompoundTerm):
        children = [
            convert_tarski_formula(env, fluents, objects, action_parameters,
                                   types, f) for f in formula.subterms
        ]
        if is_atom(formula):
            symbol = formula.predicate.symbol
        else:
            symbol = formula.symbol.name
        if symbol == BuiltinPredicateSymbol.EQ:
            assert len(children) == 2
            return em.Equals(children[0], children[1])
        elif symbol == BuiltinPredicateSymbol.NE:
            assert len(children) == 2
            return em.Not(em.Equals(children[0], children[1]))
        elif symbol == BuiltinPredicateSymbol.LT:
            assert len(children) == 2
            return em.LT(children[0], children[1])
        elif symbol == BuiltinPredicateSymbol.LE:
            assert len(children) == 2
            return em.LE(children[0], children[1])
        elif symbol == BuiltinPredicateSymbol.GT:
            assert len(children) == 2
            return em.GT(children[0], children[1])
        elif symbol == BuiltinPredicateSymbol.GE:
            assert len(children) == 2
            return em.GE(children[0], children[1])
        elif symbol == BuiltinFunctionSymbol.ADD:
            assert len(children) == 2
            return em.Plus(children[0], children[1])
        elif symbol == BuiltinFunctionSymbol.SUB:
            assert len(children) == 2
            return em.Minus(children[0], children[1])
        elif symbol == BuiltinFunctionSymbol.MUL:
            assert len(children) == 2
            return em.Times(children[0], children[1])
        elif symbol == BuiltinFunctionSymbol.DIV:
            assert len(children) == 2
            return em.Div(children[0], children[1])
        elif symbol in fluents:
            return fluents[symbol](*children)
        else:
            raise UPProblemDefinitionError(symbol + ' not supported!')
    elif isinstance(formula, Constant):
        if formula.sort.name == 'number':
            return em.Real(Fraction(float(formula.name)))
        elif isinstance(formula.sort, tarski.syntax.Interval):
            if formula.sort.language.is_subtype(\
                formula.sort, formula.sort.language.Integer)\
                or formula.sort.language.is_subtype(\
                    formula.sort, formula.sort.language.Natural):
                return em.Int(int(formula.name))
            elif formula.sort.language.is_subtype(\
                formula.sort, formula.sort.language.Real):
                return em.Real(Fraction(float(formula.name)))
            else:
                raise NotImplementedError
        elif formula.name in objects:
            return em.ObjectExp(objects[formula.name])
        else:
            raise UPProblemDefinitionError(formula + ' not supported!')
    elif isinstance(formula, Variable):
        if formula.symbol in action_parameters:
            return em.ParameterExp(action_parameters[formula.symbol])
        else:
            return em.VariableExp(unified_planning.model.Variable(formula.symbol, \
                cast(unified_planning.model.Type, _convert_type_and_update_dict(formula.sort, types, env.type_manager, formula.sort.language))))
    elif isinstance(formula, QuantifiedFormula):
        expression = convert_tarski_formula(env, fluents, objects,
                                            action_parameters, types,
                                            formula.formula)
        variables = [
            unified_planning.model.Variable(
                v.symbol,
                cast(
                    unified_planning.model.Type,
                    _convert_type_and_update_dict(v.sort, types,
                                                  env.type_manager,
                                                  v.sort.language)))
            for v in formula.variables
        ]
        if formula.quantifier == Quantifier.Exists:
            return em.Exists(expression, *variables)
        elif formula.quantifier == Quantifier.Forall:
            return em.Forall(expression, *variables)
        else:
            raise NotImplementedError
    elif isinstance(formula, Tautology):
        return em.TRUE()
    elif isinstance(formula, Contradiction):
        return em.FALSE()
    else:
        raise UPProblemDefinitionError(str(formula) + ' not supported!')
Beispiel #15
0
def convert_problem_from_tarski(
    env: Environment, tarski_problem: tarski.fstrips.Problem
) -> 'unified_planning.model.Problem':
    """Converts a tarski problem in a unified_planning.Problem."""
    em = env.expression_manager
    tm = env.type_manager
    lang = tarski_problem.language
    problem = unified_planning.model.Problem(tarski_problem.name)

    # Convert types
    types: Dict[str, Optional['unified_planning.model.Type']] = {}
    uses_object_type: bool = _check_if_tarski_problem_uses_object_type(
        tarski_problem)
    if not uses_object_type:
        types[
            'object'] = None  # we set object as None, so when it is the father of a type in tarski, in UP it will be None.
    for t in lang.sorts:
        #types will be filled with the needed types in this loop.
        _convert_type_and_update_dict(t, types, tm, lang)

    # Convert predicates and functions
    fluents = {}
    for p in lang.predicates:
        if str(p.name) in ['=', '!=', '<', '<=', '>', '>=']:
            continue
        signature: OrderedDict[str,
                               'unified_planning.model.Type'] = OrderedDict()
        for i, t in enumerate(p.sort):
            type = types[str(t.name)]
            assert type is not None
            signature[f'p{str(i+1)}'] = type
        fluent = unified_planning.model.Fluent(p.name, tm.BoolType(),
                                               signature)
        fluents[fluent.name] = fluent
        problem.add_fluent(fluent)
    for p in lang.functions:
        if str(p.name) in ['ite', '@', '+', '-', '*', '/', '**', '%', 'sqrt']:
            continue
        signature = OrderedDict()
        for i, t in enumerate(p.domain):
            type = types[str(t.name)]
            assert type is not None
            signature[f'p{str(i+1)}'] = type
        func_sort = p.sort[-1]
        if isinstance(func_sort, Interval):
            if func_sort.encode == lang.Real.encode:
                if func_sort.name == 'Real' or func_sort.name == 'number':
                    fluent = unified_planning.model.Fluent(
                        p.name, tm.RealType(), signature)
                else:
                    fluent = unified_planning.model.Fluent(p.name, tm.RealType(lower_bound=\
                        Fraction(func_sort.lower_bound), upper_bound=Fraction(func_sort.upper_bound)), signature)
            else:
                assert func_sort.encode == lang.Integer.encode or func_sort.encode == lang.Natural.encode
                if func_sort.name == 'Integer':
                    fluent = unified_planning.model.Fluent(
                        p.name, tm.IntType(), signature)
                elif func_sort.name == 'Natual':
                    fluent = unified_planning.model.Fluent(
                        p.name, tm.IntType(lower_bound=0), signature)
                else:
                    fluent = unified_planning.model.Fluent(p.name, tm.IntType(lower_bound=\
                        func_sort.lower_bound, upper_bound=func_sort.upper_bound), signature)
        else:
            fluent = unified_planning.model.Fluent(p.name,
                                                   types[func_sort.name],
                                                   signature)
        fluents[fluent.name] = fluent
        problem.add_fluent(fluent)

    # Convert objects
    objects = {}
    for c in lang.constants():
        type = types[str(c.sort.name)]
        assert type is not None
        o = unified_planning.model.Object(str(c.name), type)
        objects[o.name] = o
        problem.add_object(o)

    # Convert actions
    for a_name in tarski_problem.actions:
        a = tarski_problem.get_action(a_name)
        parameters: OrderedDict[str,
                                'unified_planning.model.Type'] = OrderedDict()
        for p in a.parameters:
            type = types[str(p.sort.name)]
            assert type is not None
            parameters[p.symbol] = type
        action = unified_planning.model.InstantaneousAction(a_name, parameters)
        action_parameters = {}
        for p in parameters.keys():
            action_parameters[p] = action.parameter(p)
        f = convert_tarski_formula(env, fluents, objects, action_parameters,
                                   types, a.precondition)
        action.add_precondition(f)
        for eff in a.effects:
            condition = convert_tarski_formula(env, fluents, objects,
                                               action_parameters, types,
                                               eff.condition)
            if isinstance(eff, AddEffect):
                f = convert_tarski_formula(env, fluents, objects,
                                           action_parameters, types, eff.atom)
                action.add_effect(f, True, condition)
            elif isinstance(eff, DelEffect):
                f = convert_tarski_formula(env, fluents, objects,
                                           action_parameters, types, eff.atom)
                action.add_effect(f, False, condition)
            elif isinstance(eff, FunctionalEffect):
                lhs = convert_tarski_formula(env, fluents, objects,
                                             action_parameters, types, eff.lhs)
                rhs = convert_tarski_formula(env, fluents, objects,
                                             action_parameters, types, eff.rhs)
                action.add_effect(lhs, rhs, condition)
            else:
                raise UPProblemDefinitionError(eff + ' not supported!')
        problem.add_action(action)

    # Set initial values
    initial_values = {}
    for fluent in fluents.values():
        l = [problem.objects_hierarchy(p.type) for p in fluent.signature]
        if fluent.type.is_bool_type():
            default_value = em.FALSE()
        elif fluent.type.is_real_type():
            default_value = em.Real(Fraction(0))
        elif fluent.type.is_int_type():
            default_value = em.Int(0)
        elif fluent.type.is_user_type():
            continue
        if len(l) == 0:
            initial_values[em.FluentExp(fluent)] = default_value
        else:
            for args in itertools.product(*l):
                initial_values[fluent(*args)] = default_value
    for i in tarski_problem.init.as_atoms():
        if isinstance(i, tuple):
            lhs = convert_tarski_formula(env, fluents, objects, {}, types,
                                         i[0])
            rhs = convert_tarski_formula(env, fluents, objects, {}, types,
                                         i[1])
            initial_values[lhs] = rhs
        else:
            f = convert_tarski_formula(env, fluents, objects, {}, types, i)
            initial_values[f] = em.TRUE()
    for lhs, rhs in initial_values.items():
        problem.set_initial_value(lhs, rhs)

    # Convert goals
    problem.add_goal(
        convert_tarski_formula(env, fluents, objects, {}, types,
                               tarski_problem.goal))

    return problem
Beispiel #16
0
 def add_action(self, action: 'up.model.action.Action'):
     '''Adds the given action.'''
     if self.has_name(action.name):
         raise UPProblemDefinitionError('Name ' + action.name +
                                        ' already defined!')
     self._actions.append(action)