def _convert_action_instance( self, msg: proto.ActionInstance, problem: Problem ) -> Union[Tuple[model.timing.Timing, unified_planning.plan.ActionInstance, model.timing.Duration], unified_planning.plan.ActionInstance]: # action instance paramaters are atoms but in UP they are FNodes # converting to up.model.FNode parameters = tuple( [self.convert(param, problem) for param in msg.parameters]) action_instance = unified_planning.plan.ActionInstance( problem.action(msg.action_name), parameters, ) start_time = (self.convert(msg.start_time) if msg.HasField("start_time") else None) end_time = self.convert( msg.end_time) if msg.HasField("end_time") else None if start_time is not None: return ( start_time, # Absolute Start Time action_instance, end_time - start_time if end_time else None, # Duration ) else: return action_instance
def _convert_metric( self, msg: proto.Metric, problem: Problem ) -> Union[metrics.MinimizeActionCosts, metrics.MinimizeSequentialPlanLength, metrics.MinimizeMakespan, metrics.MinimizeExpressionOnFinalState, metrics.MaximizeExpressionOnFinalState]: if msg.kind == proto.Metric.MINIMIZE_ACTION_COSTS: costs = {} for a, cost in msg.action_costs.items(): costs[problem.action(a)] = self.convert(cost, problem) return metrics.MinimizeActionCosts( costs=costs, default=self.convert(msg.default_action_cost, problem) if msg.HasField("default_action_cost") else None, ) elif msg.kind == proto.Metric.MINIMIZE_SEQUENTIAL_PLAN_LENGTH: return metrics.MinimizeSequentialPlanLength() elif msg.kind == proto.Metric.MINIMIZE_MAKESPAN: return metrics.MinimizeMakespan() elif msg.kind == proto.Metric.MINIMIZE_EXPRESSION_ON_FINAL_STATE: return metrics.MinimizeExpressionOnFinalState( expression=self.convert(msg.expression, problem)) elif msg.kind == proto.Metric.MAXIMIZE_EXPRESSION_ON_FINAL_STATE: return metrics.MaximizeExpressionOnFinalState( expression=self.convert(msg.expression, problem)) else: raise UPException(f"Unknown metric kind `{msg.kind}`")
def _convert_atom( self, msg: proto.Atom, problem: Problem ) -> Union[model.FNode, model.Fluent, model.Object]: field = msg.WhichOneof("content") value = getattr(msg, field) if field == "int": return problem.env.expression_manager.Int(value) elif field == "real": return problem.env.expression_manager.Real( fractions.Fraction(value.numerator, value.denominator)) elif field == "boolean": return problem.env.expression_manager.Bool(value) else: # If atom symbols, return the equivalent UP alternative # Note that parameters are directly handled at expression level if problem.has_object(value): return problem.env.expression_manager.ObjectExp( obj=problem.object(value)) else: return problem.fluent(value)
def convert_type_str(s: str, problem: Problem) -> model.types.Type: if s == "bool": return problem.env.type_manager.BoolType() elif s == "integer": return problem.env.type_manager.IntType() elif "integer[" in s: lb = int(s.split("[")[1].split(",")[0]) ub = int(s.split(",")[1].split("]")[0]) return problem.env.type_manager.IntType(lb, ub) elif s == "real": return problem.env.type_manager.RealType() elif "real[" in s: return problem.env.type_manager.RealType( lower_bound=fractions.Fraction(s.split("[")[1].split(",")[0]), upper_bound=fractions.Fraction(s.split(",")[1].split("]")[0]), ) else: if " - " in s: return problem.user_type(s.split(" - ")[0]) else: return problem.env.type_manager.UserType(s)
def _convert_type_declaration(self, msg: proto.TypeDeclaration, problem: Problem) -> model.Type: if msg.type_name == "bool": return problem.env.type_manager.BoolType() elif msg.type_name.startswith("integer["): tmp = msg.type_name.split("[")[1].split("]")[0].split(", ") return problem.env.type_manager.IntType( lower_bound=int(tmp[0]) if tmp[0] != "-inf" else None, upper_bound=int(tmp[1]) if tmp[1] != "inf" else None, ) elif msg.type_name.startswith("real["): tmp = msg.type_name.split("[")[1].split("]")[0].split(", ") lower_bound = fractions.Fraction( tmp[0]) if tmp[0] != "-inf" else None upper_bound = fractions.Fraction( tmp[1]) if tmp[1] != "inf" else None return problem.env.type_manager.RealType(lower_bound=lower_bound, upper_bound=upper_bound) else: parent = None if msg.parent_type != "": parent = problem.user_type(msg.parent_type) return problem.env.type_manager.UserType(msg.type_name, parent)
def get_rewritten_problem(self) -> Problem: '''Creates a problem that is a copy of the original problem but every ngeative fluent into action preconditions or overall goal is replaced by the fluent representing his negative.''' if self._new_problem is not None: return self._new_problem if self._problem.kind.has_simulated_effects(): # type: ignore raise up.exceptions.UPUsageError( 'NegativeConditionsRemover does not work with simulated effects' ) #NOTE that a different environment might be needed when multi-threading self._new_problem = Problem(f'{self._name}_{self._problem.name}', self._env) for o in self._problem.all_objects: self._new_problem.add_object(o) assert self._new_problem is not None name_action_map: Dict[str, Union[InstantaneousAction, DurativeAction]] = {} for action in self._problem.actions: if isinstance(action, InstantaneousAction): new_action = action.clone() new_action.name = self.get_fresh_name(action.name) new_action.clear_preconditions() for p in action.preconditions: np = self._fluent_remover.remove_negative_fluents(p) new_action.add_precondition(np) for ce in new_action.conditional_effects: ce.set_condition( self._fluent_remover.remove_negative_fluents( ce.condition)) name_action_map[action.name] = new_action elif isinstance(action, DurativeAction): new_durative_action = action.clone() new_durative_action.name = self.get_fresh_name(action.name) new_durative_action.clear_conditions() for i, cl in action.conditions.items(): for c in cl: nc = self._fluent_remover.remove_negative_fluents(c) new_durative_action.add_condition(i, nc) for t, cel in new_durative_action.conditional_effects.items(): for ce in cel: ce.set_condition( self._fluent_remover.remove_negative_fluents( ce.condition)) name_action_map[action.name] = new_durative_action else: raise NotImplementedError for t, el in self._problem.timed_effects.items(): for e in el: self._new_problem._add_effect_instance(t, e.clone()) for t, el in self._new_problem.timed_effects.items(): for e in el: if e.is_conditional(): e.set_condition( self._fluent_remover.remove_negative_fluents( e.condition)) for i, gl in self._problem.timed_goals.items(): for g in gl: ng = self._fluent_remover.remove_negative_fluents(g) self._new_problem.add_timed_goal(i, ng) for g in self._problem.goals: ng = self._fluent_remover.remove_negative_fluents(g) self._new_problem.add_goal(ng) #fluent_mapping is the map between a fluent and it's negation, when the # negation is None it means the fluent is never found in a negation into # every condititon analized before; therefore it does not need to exist. fluent_mapping = self._fluent_remover.fluent_mapping for f in self._problem.fluents: self._new_problem.add_fluent(f) fneg = fluent_mapping.get(f, None) if fneg is not None: self._new_problem.add_fluent(fneg) for fl, v in self._problem.initial_values.items(): fneg = fluent_mapping.get(fl.fluent(), None) self._new_problem.set_initial_value(fl, v) if fneg is not None: if v.bool_constant_value(): self._new_problem.set_initial_value( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), self._env.expression_manager.FALSE()) else: self._new_problem.set_initial_value( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), self._env.expression_manager.TRUE()) for action in self._problem.actions: if isinstance(action, InstantaneousAction): new_action = name_action_map[action.name] new_effects: List[Effect] = [] for e in new_action.effects: fl, v = e.fluent, e.value fneg = fluent_mapping.get(fl.fluent(), None) if fneg is not None: simplified_not_v = self._simplifier.simplify( self._env.expression_manager.Not(v)) new_effects.append( Effect( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), simplified_not_v, e.condition, e.kind)) for ne in new_effects: new_action._add_effect_instance(ne) self._new_problem.add_action(new_action) self._old_to_new[action] = [new_action] self._new_to_old[new_action] = action elif isinstance(action, DurativeAction): new_durative_action = name_action_map[action.name] new_durative_action.set_duration_constraint(action.duration) for t, el in new_durative_action.effects.items(): for e in el: fl, v = e.fluent, e.value fneg = fluent_mapping.get(fl.fluent(), None) if fneg is not None: simplified_not_v = self._simplifier.simplify( self._env.expression_manager.Not(v)) new_durative_action._add_effect_instance( t, Effect( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), simplified_not_v, e.condition, e.kind)) self._new_problem.add_action(new_durative_action) self._old_to_new[action] = [new_durative_action] self._new_to_old[new_durative_action] = action else: raise NotImplementedError for t, el in self._new_problem.timed_effects.items(): for e in el: fl, v = e.fluent, e.value fneg = fluent_mapping.get(fl.fluent(), None) if fneg is not None: simplified_not_v = self._simplifier.simplify( self._env.expression_manager.Not(v)) self._new_problem._add_effect_instance( t, Effect( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), simplified_not_v, e.condition, e.kind)) return self._new_problem
class NegativeConditionsRemover(Transformer): '''Negative conditions remover class: this class requires a problem and offers the capability to transform a problem with negative conditions into one without negative conditions. This is done by substituting every fluent that appears with a Not into the conditions with different fluent representing his negation.''' def __init__(self, problem: Problem, name: str = 'ncrm'): Transformer.__init__(self, problem, name) #NOTE no simplification are made. But it's possible to add them in key points self._fluent_remover = NegativeFluentRemover(self, self._env) #Represents the map from the new action to the old action self._new_to_old: Dict[Action, Action] = {} #represents a mapping from the action of the original problem to action of the new one. self._old_to_new: Dict[Action, List[Action]] = {} def get_rewritten_problem(self) -> Problem: '''Creates a problem that is a copy of the original problem but every ngeative fluent into action preconditions or overall goal is replaced by the fluent representing his negative.''' if self._new_problem is not None: return self._new_problem if self._problem.kind.has_simulated_effects(): # type: ignore raise up.exceptions.UPUsageError( 'NegativeConditionsRemover does not work with simulated effects' ) #NOTE that a different environment might be needed when multi-threading self._new_problem = Problem(f'{self._name}_{self._problem.name}', self._env) for o in self._problem.all_objects: self._new_problem.add_object(o) assert self._new_problem is not None name_action_map: Dict[str, Union[InstantaneousAction, DurativeAction]] = {} for action in self._problem.actions: if isinstance(action, InstantaneousAction): new_action = action.clone() new_action.name = self.get_fresh_name(action.name) new_action.clear_preconditions() for p in action.preconditions: np = self._fluent_remover.remove_negative_fluents(p) new_action.add_precondition(np) for ce in new_action.conditional_effects: ce.set_condition( self._fluent_remover.remove_negative_fluents( ce.condition)) name_action_map[action.name] = new_action elif isinstance(action, DurativeAction): new_durative_action = action.clone() new_durative_action.name = self.get_fresh_name(action.name) new_durative_action.clear_conditions() for i, cl in action.conditions.items(): for c in cl: nc = self._fluent_remover.remove_negative_fluents(c) new_durative_action.add_condition(i, nc) for t, cel in new_durative_action.conditional_effects.items(): for ce in cel: ce.set_condition( self._fluent_remover.remove_negative_fluents( ce.condition)) name_action_map[action.name] = new_durative_action else: raise NotImplementedError for t, el in self._problem.timed_effects.items(): for e in el: self._new_problem._add_effect_instance(t, e.clone()) for t, el in self._new_problem.timed_effects.items(): for e in el: if e.is_conditional(): e.set_condition( self._fluent_remover.remove_negative_fluents( e.condition)) for i, gl in self._problem.timed_goals.items(): for g in gl: ng = self._fluent_remover.remove_negative_fluents(g) self._new_problem.add_timed_goal(i, ng) for g in self._problem.goals: ng = self._fluent_remover.remove_negative_fluents(g) self._new_problem.add_goal(ng) #fluent_mapping is the map between a fluent and it's negation, when the # negation is None it means the fluent is never found in a negation into # every condititon analized before; therefore it does not need to exist. fluent_mapping = self._fluent_remover.fluent_mapping for f in self._problem.fluents: self._new_problem.add_fluent(f) fneg = fluent_mapping.get(f, None) if fneg is not None: self._new_problem.add_fluent(fneg) for fl, v in self._problem.initial_values.items(): fneg = fluent_mapping.get(fl.fluent(), None) self._new_problem.set_initial_value(fl, v) if fneg is not None: if v.bool_constant_value(): self._new_problem.set_initial_value( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), self._env.expression_manager.FALSE()) else: self._new_problem.set_initial_value( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), self._env.expression_manager.TRUE()) for action in self._problem.actions: if isinstance(action, InstantaneousAction): new_action = name_action_map[action.name] new_effects: List[Effect] = [] for e in new_action.effects: fl, v = e.fluent, e.value fneg = fluent_mapping.get(fl.fluent(), None) if fneg is not None: simplified_not_v = self._simplifier.simplify( self._env.expression_manager.Not(v)) new_effects.append( Effect( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), simplified_not_v, e.condition, e.kind)) for ne in new_effects: new_action._add_effect_instance(ne) self._new_problem.add_action(new_action) self._old_to_new[action] = [new_action] self._new_to_old[new_action] = action elif isinstance(action, DurativeAction): new_durative_action = name_action_map[action.name] new_durative_action.set_duration_constraint(action.duration) for t, el in new_durative_action.effects.items(): for e in el: fl, v = e.fluent, e.value fneg = fluent_mapping.get(fl.fluent(), None) if fneg is not None: simplified_not_v = self._simplifier.simplify( self._env.expression_manager.Not(v)) new_durative_action._add_effect_instance( t, Effect( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), simplified_not_v, e.condition, e.kind)) self._new_problem.add_action(new_durative_action) self._old_to_new[action] = [new_durative_action] self._new_to_old[new_durative_action] = action else: raise NotImplementedError for t, el in self._new_problem.timed_effects.items(): for e in el: fl, v = e.fluent, e.value fneg = fluent_mapping.get(fl.fluent(), None) if fneg is not None: simplified_not_v = self._simplifier.simplify( self._env.expression_manager.Not(v)) self._new_problem._add_effect_instance( t, Effect( self._env.expression_manager.FluentExp( fneg, tuple(fl.args)), simplified_not_v, e.condition, e.kind)) return self._new_problem def get_original_action(self, action: Action) -> Action: '''After the method get_rewritten_problem is called, this function maps the actions of the transformed problem into the actions of the original problem.''' return self._new_to_old[action] def get_transformed_actions(self, action: Action) -> List[Action]: '''After the method get_rewritten_problem is called, this function maps the actions of the original problem into the actions of the transformed problem.''' return self._old_to_new[action]
def _convert_problem(self, msg: proto.Problem, env: Optional[Environment] = None) -> Problem: problem = Problem(name=msg.problem_name, env=env) for t in msg.types: problem._add_user_type(self.convert(t, problem)) for obj in msg.objects: problem.add_object(self.convert(obj, problem)) for f in msg.fluents: problem.add_fluent( self.convert(f, problem), default_initial_value=self.convert(f.default_value, problem) if f.HasField("default_value") else None, ) for f in msg.actions: problem.add_action(self.convert(f, problem)) for eff in msg.timed_effects: ot = self.convert(eff.occurrence_time, problem) effect = self.convert(eff.effect, problem) problem.add_timed_effect( timing=ot, fluent=effect.fluent, value=effect.value, condition=effect.condition, ) for assign in msg.initial_state: problem.set_initial_value( fluent=self.convert(assign.fluent, problem), value=self.convert(assign.value, problem), ) for g in msg.goals: goal = self.convert(g.goal, problem) if str(g.timing) == "": problem.add_goal(goal) else: timing = self.convert(g.timing) problem.add_timed_goal(interval=timing, goal=goal) for metric in msg.metrics: problem.add_quality_metric(self.convert(metric, problem)) return problem