Skip to content

Commit

Permalink
feat: check terms consistency wrt type tag
Browse files Browse the repository at this point in the history
i.e. terms with the same name should have the same type tags.
  • Loading branch information
marcofavorito committed Jun 9, 2023
1 parent b8e0a2b commit 3661f64
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 20 deletions.
2 changes: 1 addition & 1 deletion pddl/helpers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def ensure_set(arg: Optional[Collection], immutable: bool = True) -> AbstractSet
return op(arg) if arg is not None else op()


def check_no_duplicates(arg: Optional[Sequence[str]]) -> Optional[Collection]:
def check_no_duplicates(arg: Optional[Collection]) -> Optional[Collection]:
"""Check that the argument is a set."""
if arg is None:
return None
Expand Down
32 changes: 26 additions & 6 deletions pddl/logic/predicates.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,45 @@

"""This class implements PDDL predicates."""
import functools
from typing import Sequence
from typing import Dict, Sequence, Set

from pddl.custom_types import namelike, parse_name
from pddl.helpers.base import assert_
from pddl.custom_types import name, namelike, parse_name
from pddl.helpers.base import assert_, check
from pddl.helpers.cache_hash import cache_hash
from pddl.logic.base import Atomic, Formula
from pddl.logic.terms import Term
from pddl.logic.terms import Term, _print_tag_set
from pddl.parser.symbols import Symbols


def _check_terms_consistency(terms: Sequence[Term]):
"""
Check that the term sequence have consistent type tags.
In particular, terms with the same name must have the same type tags.
"""
seen: Dict[name, Set[name]] = {}
for term in terms:
if term.name not in seen:
seen[term.name] = set(term.type_tags)
else:
check(
seen[term.name] == set(term.type_tags),
f"Term {term} has inconsistent type tags: "
f"previous type tags {_print_tag_set(seen[term.name])}, new type tags {_print_tag_set(term.type_tags)}",
exception_cls=ValueError,
)


@cache_hash
@functools.total_ordering
class Predicate(Atomic):
"""A class for a Predicate in PDDL."""

def __init__(self, name: namelike, *terms: Term):
def __init__(self, predicate_name: namelike, *terms: Term):
"""Initialize the predicate."""
self._name = parse_name(name)
self._name = parse_name(predicate_name)
self._terms = tuple(terms)
_check_terms_consistency(self._terms)

@property
def name(self) -> str:
Expand Down
15 changes: 11 additions & 4 deletions pddl/logic/terms.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,33 @@
from pddl.helpers.cache_hash import cache_hash


def _print_tag_set(type_tags: AbstractSet[name_type]) -> str:
"""Print a tag set."""
if len(type_tags) == 0:
return "[]"
return repr(sorted(map(str, type_tags)))


@cache_hash
@functools.total_ordering
class Term:
"""A term in a formula."""

def __init__(
self, name: namelike, type_tags: Optional[Collection[namelike]] = None
self, term_name: namelike, type_tags: Optional[Collection[namelike]] = None
):
"""
Initialize a term.
:param name: the name for the term.
:param term_name: the name for the term.
:param type_tags: the type tags associated to this term.
"""
assert_(type(self) is not Term, "Term is an abstract class")
self._name = parse_name(name)
self._name = parse_name(term_name)
self._type_tags = frozenset(to_type(ensure_set(check_no_duplicates(type_tags)))) # type: ignore

@property
def name(self) -> str:
def name(self) -> name_type:
"""Get the name."""
return self._name

Expand Down
11 changes: 3 additions & 8 deletions pddl/parser/typed_list_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from pddl.custom_types import name, parse_name, parse_type
from pddl.helpers.base import check, safe_index
from pddl.logic.terms import _print_tag_set
from pddl.parser.symbols import Symbols


Expand Down Expand Up @@ -203,7 +204,7 @@ def _check_item_types(self, item_name: name, type_tags: Set[name]) -> None:
if previous_type_tags != type_tags:
raise ValueError(
f"invalid types for item '{item_name}': previous known tags were "
f"{self._print_tag_set(previous_type_tags)}, got {self._print_tag_set(type_tags)}"
f"{_print_tag_set(previous_type_tags)}, got {_print_tag_set(type_tags)}"
)

def _add_item(self, item_name: name, type_tags: Set[name]) -> None:
Expand All @@ -219,11 +220,5 @@ def _raise_multiple_types_error(
"""Raise an error if the item has multiple types."""
raise ValueError(
f"typed list names should not have more than one type, got '{item_name}' with "
f"types {self._print_tag_set(type_tags)}"
f"types {_print_tag_set(type_tags)}"
)

def _print_tag_set(self, type_tags: Set[name]) -> str:
"""Print a tag set."""
if len(type_tags) == 0:
return "[]"
return repr(sorted(map(str, type_tags)))
28 changes: 28 additions & 0 deletions tests/test_logic/test_predicates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#
# 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.
#

"""Test pddl.logic.predicates module."""
import pytest

from pddl.logic import Predicate
from pddl.logic.terms import Variable


def test_inconsistent_predicate_terms() -> None:
"""Test that terms of a predicate must have consistent typing."""
with pytest.raises(
ValueError,
match=r"Term \?a has inconsistent type tags: previous type tags \['t1', 't2'\], new type tags "
r"\['t3', 't4'\]",
):
a1, a2 = Variable("a", ["t1", "t2"]), Variable("a", ["t3", "t4"])
Predicate("p", a1, a2)
2 changes: 1 addition & 1 deletion tests/test_logic/test_terms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# https://opensource.org/licenses/MIT.
#

"""Test pddl.logic module."""
"""Test pddl.logic.terms module."""
import pytest

from pddl.logic.terms import Variable
Expand Down

0 comments on commit 3661f64

Please sign in to comment.