Skip to content

Commit

Permalink
Merge pull request #29 from whitemech/feat/derived-predicates-and-con…
Browse files Browse the repository at this point in the history
…ditional-effects

Add support for derived predicates and conditional effects
  • Loading branch information
francescofuggitti authored Aug 8, 2022
2 parents b869df8 + 41a8531 commit 455503c
Show file tree
Hide file tree
Showing 18 changed files with 850 additions and 432 deletions.
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ vulture: ## check unused code

darglint: ## check docstrings
darglint pddl scripts/whitelist.py
darglint aea

test: ## run tests quickly with the default Python
pytest tests --doctest-modules \
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mistune = "==2.0.0a4"
mkdocs-material = "*"
types-click = "*"
vulture = "*"
darglint = "==1.8.0"

[requires]
python_version = "3.7"
684 changes: 380 additions & 304 deletions Pipfile.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,14 @@ requirements:
- [ ] `:existential-preconditions`
- [ ] `:universal-preconditions`
- [ ] `:quantified-preconditions`
- [ ] `:conditional-effects`
- [x] `:conditional-effects`
- [ ] `:fluents`
- [ ] `:numeric-fluents`
- [x] `:non-deterministic` (see [6th IPC: Uncertainty Part](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.163.7140&rep=rep1&type=pdf))
- [ ] `:adl`
- [ ] `:durative-actions`
- [ ] `:duration-inequalities`
- [ ] `:derived-predicates`
- [x] `:derived-predicates`
- [ ] `:timed-initial-literals`
- [ ] `:preferences`
- [ ] `:constraints`
Expand Down
59 changes: 43 additions & 16 deletions pddl/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@
"""
import functools
from enum import Enum
from typing import AbstractSet, Collection, Optional, Sequence, cast
from typing import AbstractSet, Collection, Optional, Sequence, Set, cast

from pddl.custom_types import name as name_type
from pddl.custom_types import namelike, to_names
from pddl.helpers.base import assert_, ensure, ensure_sequence, ensure_set
from pddl.logic.base import FalseFormula, Formula, TrueFormula, is_literal
from pddl.logic.predicates import Predicate
from pddl.logic.predicates import DerivedPredicate, Predicate
from pddl.logic.terms import Constant, Variable
from pddl.parser.symbols import RequirementSymbols as RS


class Domain:
Expand All @@ -47,6 +48,9 @@ def __init__(
types: Optional[Collection[namelike]] = None,
constants: Optional[Collection[Constant]] = None,
predicates: Optional[Collection[Predicate]] = None, # TODO cannot be empty
derived_predicates: Optional[
Collection[DerivedPredicate]
] = None, # TODO cannot be empty
actions: Optional[Collection["Action"]] = None,
):
"""
Expand All @@ -57,13 +61,15 @@ def __init__(
:param types: the list of supported types.
:param constants: the constants.
:param predicates: the predicates.
:param derived_predicates: the derived predicates.
:param actions: the actions.
"""
self._name = name_type(name)
self._requirements = ensure_set(requirements)
self._types = set(to_names(ensure_set(types)))
self._constants = ensure_set(constants)
self._predicates = ensure_set(predicates)
self._derived_predicates = ensure_set(derived_predicates)
self._actions = ensure_set(actions)

@property
Expand All @@ -86,6 +92,11 @@ def predicates(self) -> AbstractSet[Predicate]:
"""Get the predicates."""
return self._predicates

@property
def derived_predicates(self) -> AbstractSet[DerivedPredicate]:
"""Get the derived predicates."""
return self._derived_predicates

@property
def actions(self) -> AbstractSet["Action"]:
"""Get the actions."""
Expand All @@ -105,6 +116,7 @@ def __eq__(self, other):
and self.types == other.types
and self.constants == other.constants
and self.predicates == other.predicates
and self.derived_predicates == other.derived_predicates
and self.actions == other.actions
)

Expand All @@ -127,7 +139,7 @@ def __init__(
:param name: the name of the PDDL problem.
:param domain: the PDDL domain.
:param domain: the domain name. Must match with the domain object.
:param domain_name: the domain name. Must match with the domain object.
:param requirements: the set of PDDL requirements.
:param objects: the set of objects.
:param init: the initial condition.
Expand Down Expand Up @@ -242,8 +254,8 @@ def __init__(
"""
self._name: str = name_type(name)
self._parameters: Sequence[Variable] = ensure_sequence(parameters)
self._precondition: Formula = ensure(precondition, FalseFormula())
self._effect: Formula = ensure(effect, FalseFormula())
self._precondition = precondition
self._effect = effect

@property
def name(self) -> str:
Expand All @@ -256,21 +268,23 @@ def parameters(self) -> Sequence[Variable]:
return self._parameters

@property
def precondition(self) -> Formula:
def precondition(self) -> Optional[Formula]:
"""Get the precondition."""
return self._precondition

@property
def effect(self) -> Formula:
def effect(self) -> Optional[Formula]:
"""Get the effect."""
return self._effect

def __str__(self):
"""Get the string."""
operator_str = "(:action {0}\n".format(self.name)
operator_str += f" :parameters ({' '.join(map(str, self.parameters))})\n"
operator_str += f" :precondition {str(self.precondition)}\n"
operator_str += f" :effect {str(self.effect)}\n"
if self.precondition is not None:
operator_str += f" :precondition {str(self.precondition)}\n"
if self.effect is not None:
operator_str += f" :effect {str(self.effect)}\n"
operator_str += ")"
return operator_str

Expand All @@ -293,13 +307,26 @@ def __hash__(self):
class Requirements(Enum):
"""Enum class for the requirements."""

STRIPS = "strips"
EQUALITY = "equality"
TYPING = "typing"
ADL = "adl"
NON_DETERMINISTIC = "non-deterministic"
NEG_PRECONDITION = "negative-preconditions"
DIS_PRECONDITION = "disjunctive-preconditions"
STRIPS = RS.STRIPS.strip()
TYPING = RS.TYPING.strip()
NEG_PRECONDITION = RS.NEG_PRECONDITION.strip()
DIS_PRECONDITION = RS.DIS_PRECONDITION.strip()
EQUALITY = RS.EQUALITY.strip()
CONDITIONAL_EFFECTS = RS.CONDITIONAL_EFFECTS.strip()
ADL = RS.ADL.strip()
DERIVED_PREDICATES = RS.DERIVED_PREDICATES.strip()
NON_DETERMINISTIC = RS.NON_DETERMINISTIC.strip()

@classmethod
def strips_requirements(cls) -> Set["Requirements"]:
"""Get the STRIPS requirements."""
return {
Requirements.TYPING,
Requirements.NEG_PRECONDITION,
Requirements.DIS_PRECONDITION,
Requirements.EQUALITY,
Requirements.CONDITIONAL_EFFECTS,
}

def __str__(self) -> str:
"""Get the string representation."""
Expand Down
11 changes: 10 additions & 1 deletion pddl/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,16 @@ def domain_to_string(domain: Domain) -> str:
body += _sort_and_print_collection("(:constants ", domain.constants, ")\n")
body += _sort_and_print_collection("(:predicates ", domain.predicates, ")\n")
body += _sort_and_print_collection(
"", domain.actions, "", to_string=lambda obj: str(obj) + "\n",
"",
domain.derived_predicates,
"",
to_string=lambda obj: str(obj) + "\n",
)
body += _sort_and_print_collection(
"",
domain.actions,
"",
to_string=lambda obj: str(obj) + "\n",
)
result = result + "\n" + indent(body, indentation) + "\n)"
result = _remove_empty_lines(result)
Expand Down
2 changes: 1 addition & 1 deletion pddl/helpers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def safe_get(seq: Sequence, index: int, default=None):

def find(seq: Sequence, condition: Callable[[Any], bool]) -> int:
"""
Find the index of the first element that safisfies a condition.
Find the index of the first element that satisfies a condition.
:param seq: the sequence.
:param condition: the condition.
Expand Down
9 changes: 9 additions & 0 deletions pddl/helpers/cache_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ def _getstate(fn):
with another instance of the interpreter, the stored hash might
be inconsistent with the PYTHONHASHSEED initialization of the
new interpreter.
:param fn: the getstate function.
:return: the new getstate function.
"""

@wraps(fn)
Expand Down Expand Up @@ -84,6 +87,9 @@ def _setstate(fn):
as the state might be restored in another
interpreter in which the old hash value
might not be consistent anymore.
:param fn: the setstate function.
:return: the new setstate function.
"""

@wraps(fn)
Expand All @@ -103,6 +109,9 @@ def cache_hash(cls):
__hash__
__getstate__
__setstate__
:param cls: the class to wrap
:return: the wrapped class
"""
cls.__hash__ = _cache_hash(cls.__hash__)

Expand Down
2 changes: 1 addition & 1 deletion pddl/logic/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def is_literal(formula: Formula) -> bool:
- a Not formula whose argument is an atomic formula.
:param formula: the formula.
:return True if the formula is a literal; False otherwise.
:return: True if the formula is a literal; False otherwise.
"""
return (
isinstance(formula, Atomic)
Expand Down
Loading

0 comments on commit 455503c

Please sign in to comment.