Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autonomous processes events #629

Draft
wants to merge 27 commits into
base: master
Choose a base branch
from
Draft

Conversation

hstairs
Copy link
Contributor

@hstairs hstairs commented Sep 18, 2024

In this branch we add support for PDDL+ specification. The branch includes extensions to the problem representation (so allowing to specify processes and events), pddl writer (so allowing to use domain independent off-the-shelf PDDL+ solvers), pddl reader (so allowing get PDDL+ specifications into up data structures). There is some design choice that would need discussion though.

@hstairs hstairs requested a review from alvalentini September 18, 2024 09:01
@hstairs hstairs self-assigned this Sep 19, 2024
@hstairs hstairs linked an issue Sep 19, 2024 that may be closed by this pull request
Copy link
Member

@mikand mikand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hstairs Thanks fo this, it looks good but there are few things I would do before merging:

  1. Add some tests in the examples showing the new feature
  2. Add EVENTS to the kind and handle it
  3. Do not inherit the Event class from InstantaneousAction, in many places we check with isinstance(x, InstantaneousAction)
  4. I am ok with limiting ourselves with linear derivatives for now, but I would design the interface of processes to be generic, otherwise it is very hard to change it in the future. Also, the parser and the code should raise meaningful exceptions when a non-linear case is encountered, now they operate wrongly.

@@ -526,6 +552,8 @@ def _parse_exp(
solved.append(self._em.FluentExp(problem.fluent(exp.value)))
elif problem.has_object(exp.value): # object
solved.append(self._em.ObjectExp(problem.object(exp.value)))
elif exp.value == "#t":
solved.append(self._em.Int(1))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to model this explicitly?
I see this works for the linear case, but how can we catch an expression like (* #t #t) and raise an exception?

I think we shall handle this in a more principled way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In PDDL+ we can express first time derivatives only, meaning that effects are always going to be (* #t exp) where exp is to be #t-free (citing from the PDDL+ paper). Note that exp is not required to be linear or am I enforcing that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having said that there is definitely a cleaner way to parse that!



class Process(Action):
"""This is the `Process` class, which implements the abstract `Process` class."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? It seems to me it implements the abstract Action class 😄

I thinkit is not too bad to have processes and events to be Actions, even though they are semantically different. In hindsight we might have called the abstract action class somethig else.
@alvalentini maybe we can consider a refactoring of this name, eventually...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could call this transition, and I have hinted at something like that to Alessandro...but it is a major change no?

Adds the given `derivative effect` to the `process's effects`.

:param fluent: The `fluent` objective of the time derivative definition.
:param value: The given `fluent` is incremented by the given `value`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is wrong, and I think that how we model derivatives is a central issue that shall be very well documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!


@property
def preconditions(self) -> List["up.model.fnode.FNode"]:
"""Returns the `list` of the `Action` `preconditions`."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the comments in this class are copy-pasted, we need to replace Action with Process

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

)


class Event(InstantaneousAction):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of dangerous: beware that isinstance(x, InstantaneousAction) would return True if x is an Event. I get that we save some code duplication, but here I would inherit from Action only... Then, maybe we can create a mixin to avoid the code duplication

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

@@ -331,6 +331,10 @@ def _get_static_and_unused_fluents(
unused_fluents.clear()
for f in se.fluents:
static_fluents.discard(f.fluent())
elif isinstance(a, up.model.action.Process):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about events here? Again, they are probably absorbed by the isinstance(a, up.model.action.InstantaneousAction), so we need to fix this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

@@ -682,6 +686,9 @@ def _kind_factory(self) -> "_KindFactory":
for goal in chain(*self._timed_goals.values(), self._goals):
factory.update_problem_kind_expression(goal)
factory.update_problem_kind_initial_state(self)
self.processes
if len(list(self.processes)) > 0:
factory.kind.set_time("PROCESSES")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add also some flags for events!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure we need to differentiate processes and events. I think a flag (likely not "processes") for both is fine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One further comment: if we only have events we need to find a way to understand the semantics. I am not sure this case is well documented in the syntax of PDDL+. For instance, ENHSP considers temporal only a problem where there is at least one process. I don't see any use for events only (as they are understood in PDDL+) as events make sense if triggered by something which is changed as an effect of some passage of time.

@@ -1003,6 +1010,8 @@ def update_problem_kind_action(
if len(action.simulated_effects) > 0:
self.kind.set_simulated_entities("SIMULATED_EFFECTS")
self.kind.set_time("CONTINUOUS_TIME")
elif isinstance(action, up.model.action.Process):
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add PROCESSES to the kind

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, connected with the previous

process_temporal_kind = ProblemKind(version=LATEST_PROBLEM_KIND_VERSION)
process_temporal_kind.set_problem_class("ACTION_BASED")
process_temporal_kind.set_time("PROCESSES")
process_temporal_kind.set_time("CONTINUOUS_TIME")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no FLAT_TYPING here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't FLAT_TYPING a restriction on the kind of types we can define (like no hierarchy)? If so, there is no restriction here, or I didn't understand this :)

Copy link

codecov bot commented Oct 6, 2024

Codecov Report

Attention: Patch coverage is 88.65979% with 44 lines in your changes missing coverage. Please review.

Project coverage is 85.38%. Comparing base (6ee4e08) to head (6e3db94).
Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
unified_planning/model/action.py 82.42% 29 Missing ⚠️
unified_planning/io/pddl_writer.py 81.81% 8 Missing ⚠️
unified_planning/io/pddl_reader.py 90.90% 4 Missing ⚠️
unified_planning/model/mixins/actions_set.py 70.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #629      +/-   ##
==========================================
+ Coverage   85.36%   85.38%   +0.01%     
==========================================
  Files         202      204       +2     
  Lines       27319    27653     +334     
==========================================
+ Hits        23322    23611     +289     
- Misses       3997     4042      +45     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@hstairs hstairs force-pushed the autonomous_processes_events branch from ea544c8 to aabe009 Compare October 16, 2024 08:12
@hstairs
Copy link
Contributor Author

hstairs commented Nov 12, 2024

I guess I addressed the requested changes and added a few more tests together with a running example. I think there can be other possible refactoring in the structure of action/event/processes/sensing/durativeactions but I run out of time and did only some of them.

@Ezpeon
Copy link

Ezpeon commented Nov 29, 2024

Hello, I started looking a bit into this and I think I could help a bit with the refactoring that was previously mentioned in the other comments.
I started another PR #646 where I will try to better organize the features (still work in progress, it does not currently pass mypy checks and other changes are needed, but pytest tests should still work).
I will let you know when it is in a state where it would make sense to look more into it to decide if it is worth it to adopt the changes I made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PDDL+
3 participants