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

Domain axioms #66

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ requirements:
- [x] `:universal-preconditions`
- [x] `:quantified-preconditions`
- [x] `:conditional-effects`
- [x] `:domain-axioms`
- [ ] `: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))
Expand Down
10 changes: 10 additions & 0 deletions pddl/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from pddl.logic.base import Formula, TrueFormula, is_literal
from pddl.logic.predicates import DerivedPredicate, Predicate
from pddl.logic.terms import Constant, Variable
from pddl.logic.axioms import Axiom
from pddl.parser.symbols import RequirementSymbols as RS


Expand All @@ -48,6 +49,7 @@ def __init__(
Collection[DerivedPredicate]
] = None, # TODO cannot be empty
actions: Optional[Collection["Action"]] = None,
axioms: Optional[Collection["Axiom"]] = None,
):
"""
Initialize a PDDL domain.
Expand All @@ -59,6 +61,7 @@ def __init__(
:param predicates: the predicates.
:param derived_predicates: the derived predicates.
:param actions: the actions.
:param axioms: the axioms.
"""
self._name = name_type(name)
self._requirements = ensure_set(requirements)
Expand All @@ -67,6 +70,7 @@ def __init__(
self._predicates = ensure_set(predicates)
self._derived_predicates = ensure_set(derived_predicates)
self._actions = ensure_set(actions)
self._axioms = ensure_set(axioms)

@property
def name(self) -> str:
Expand Down Expand Up @@ -97,6 +101,11 @@ def derived_predicates(self) -> AbstractSet[DerivedPredicate]:
def actions(self) -> AbstractSet["Action"]:
"""Get the actions."""
return self._actions

@property
def axioms(self) -> AbstractSet["Axiom"]:
"""Get the actions."""
return self._axioms

@property
def types(self) -> AbstractSet[name_type]:
Expand Down Expand Up @@ -315,6 +324,7 @@ class Requirements(Enum):
ADL = RS.ADL.strip()
DERIVED_PREDICATES = RS.DERIVED_PREDICATES.strip()
NON_DETERMINISTIC = RS.NON_DETERMINISTIC.strip()
DOMAIN_AXIOMS = RS.DOMAIN_AXIOMS.strip()

@classmethod
def strips_requirements(cls) -> Set["Requirements"]:
Expand Down
86 changes: 86 additions & 0 deletions pddl/logic/axioms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
#
# Copyright 2021-2023 WhiteMech
#
# ------------------------------
#
# This file is part of pddl.
#
# Use of this source code is governed by an MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.
#

"""
Core module of the package.

It contains the class definitions to build and modify PDDL domains or problems.
"""
from typing import Optional, Sequence

from pddl.helpers.base import (
_typed_parameters,
ensure_sequence,
)
from pddl.logic.base import Formula
from pddl.logic.terms import Variable


class Axiom:
"""A class for the PDDL Axiom."""

def __init__(
self,
vars: Sequence[Variable],
context: Optional[Formula] = None,
implies: Optional[Formula] = None,
):
"""
Initialize the axiom.

:param vars: the axiom parameters.
:param context: the axiom context.
:param implies: the axiom implications.
"""
self._vars: Sequence[Variable] = ensure_sequence(vars)
self._context = context
self._implies = implies

@property
def vars(self) -> Sequence[Variable]:
"""Get the variables."""
return self._vars

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

@property
def implies(self) -> Optional[Formula]:
"""Get the implications."""
return self._implies

def __str__(self):
"""Get the string."""
operator_str = "(:axiom\n"
operator_str += f" :vars ({_typed_parameters(self.vars)})\n"
if self.context is not None:
operator_str += f" :context {str(self.context)}\n"
if self.implies is not None:
operator_str += f" :implies {str(self.implies)}\n"
operator_str += ")"
return operator_str

def __eq__(self, other):
"""Check equality between two Axioms."""
return (
isinstance(other, Axiom)
and self.vars == other.vars
and self.context == other.context
and self.implies == other.implies
)

def __hash__(self):
"""Get the hash."""
return hash((self.vars, self.context, self.implies))
6 changes: 6 additions & 0 deletions pddl/parser/common.lark
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ NAME: /[a-zA-Z][a-zA-Z0-9-_]*/
| ADL
| DERIVED_PREDICATES
| CONDITIONAL_EFFECTS
| DOMAIN_AXIOMS

COMMENT: /;[^\n]*/

Expand All @@ -25,6 +26,10 @@ ACTION: ":action"
PARAMETERS: ":parameters"
PRECONDITION: ":precondition"
EFFECT: ":effect"
AXIOM: ":axiom"
VARS: ":vars"
CONTEXT: ":context"
IMPLIES: ":implies"
DERIVED: ":derived"
FORALL: "forall"
EXISTS: "exists"
Expand All @@ -51,6 +56,7 @@ CONDITIONAL_EFFECTS: ":conditional-effects"
EXISTENTIAL_PRECONDITIONS: ":existential-preconditions"
UNIVERSAL_PRECONDITIONS: ":universal-preconditions"
QUANTIFIED_PRECONDITIONS: ":quantified-preconditions"
DOMAIN_AXIOMS: ":domain-axioms"

// others
LPAR : "("
Expand Down
10 changes: 9 additions & 1 deletion pddl/parser/domain.lark
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ predicates: LPAR PREDICATES atomic_formula_skeleton+ RPAR
atomic_formula_skeleton: LPAR NAME typed_list_variable RPAR


?structure_def: action_def | derived_predicates
?structure_def: action_def | derived_predicates | axiom_def
action_def: LPAR ACTION NAME PARAMETERS action_parameters action_body_def RPAR
action_parameters: LPAR typed_list_variable RPAR
?action_body_def: [PRECONDITION emptyor_pregd] [EFFECT emptyor_effect]
derived_predicates: LPAR DERIVED atomic_formula_skeleton gd RPAR

axiom_def: LPAR AXIOM VARS axiom_vars axiom_body_def RPAR
axiom_vars: LPAR typed_list_variable RPAR
?axiom_body_def: [CONTEXT emptyor_pregd] [IMPLIES emptyor_effect]

// preconditions
emptyor_pregd: LPAR RPAR
| gd
Expand Down Expand Up @@ -76,6 +80,10 @@ type_def: LPAR EITHER primitive_type+ RPAR
%import .common.PARAMETERS -> PARAMETERS
%import .common.PRECONDITION -> PRECONDITION
%import .common.EFFECT -> EFFECT
%import .common.AXIOM -> AXIOM
%import .common.VARS -> VARS
%import .common.CONTEXT -> CONTEXT
%import .common.IMPLIES -> IMPLIES
%import .common.DERIVED -> DERIVED
%import .common.FORALL -> FORALL
%import .common.EXISTS -> EXISTS
Expand Down
27 changes: 26 additions & 1 deletion pddl/parser/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from pddl.logic.effects import AndEffect, Forall, When
from pddl.logic.predicates import DerivedPredicate, EqualTo, Predicate
from pddl.logic.terms import Constant, Variable
from pddl.logic.axioms import Axiom
from pddl.parser import DOMAIN_GRAMMAR_FILE, PARSERS_DIRECTORY
from pddl.parser.symbols import Symbols

Expand All @@ -58,16 +59,19 @@ def domain(self, args):
args = [arg for arg in args if arg is not None]
kwargs = {}
actions = []
axioms = []
derived_predicates = []
for arg in args[2:-1]:
if isinstance(arg, Action):
actions.append(arg)
elif isinstance(arg, Axiom):
axioms.append(arg)
elif isinstance(arg, DerivedPredicate):
derived_predicates.append(arg)
else:
assert_(isinstance(arg, dict))
kwargs.update(arg)
kwargs.update(actions=actions, derived_predicates=derived_predicates)
kwargs.update(actions=actions, axioms=axioms, derived_predicates=derived_predicates)
return Domain(**kwargs)

def domain_def(self, args):
Expand Down Expand Up @@ -127,6 +131,27 @@ def action_parameters(self, args):
name: Variable(name, tags) for name, tags in args[1].items()
}
return list(self._current_parameters_by_name.values())

def axiom_def(self, args):
"""Process the 'action_def' rule."""
if not ({Requirements.DOMAIN_AXIOMS} & self._extended_requirements):
raise PDDLMissingRequirementError(Requirements.DOMAIN_AXIOMS)

_, _axiom, _vars, vars, children, _ = args

# process action body
_children = children.children
body = {
_children[i][1:]: _children[i + 1] for i in range(0, len(_children), 2)
}
return Axiom(vars, **body)

def axiom_vars(self, args):
"""Process the 'axiom_vars' rule."""
self._current_parameters_by_name = {
name: Variable(name, tags) for name, tags in args[1].items()
}
return list(self._current_parameters_by_name.values())

def emptyor_pregd(self, args):
"""Process the 'emptyor_pregd' rule."""
Expand Down
5 changes: 5 additions & 0 deletions pddl/parser/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class Symbols(Enum):
PARAMETERS = ":parameters"
PRECONDITION = ":precondition"
EFFECT = ":effect"
AXIOM = ":axiom"
VARS = ":vars"
CONTEXT = ":context"
IMPLIES = ":implies"
ROUND_BRACKET_LEFT = "("
ROUND_BRACKET_RIGHT = ")"
TYPE_SEP = "-"
Expand All @@ -69,6 +73,7 @@ class RequirementSymbols(Enum):
ADL = ":adl"
DERIVED_PREDICATES = ":derived-predicates"
NON_DETERMINISTIC = ":non-deterministic"
DOMAIN_AXIOMS = ":domain-axioms"

def strip(self) -> str:
"""Strip the leading colon."""
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"tireworld-truck",
"triangle-tireworld",
"zenotravel",
"something2-20BN"
]

DOMAIN_FILES = [
Expand Down
Loading