From 496a25486d24942fcbf4096bbd156743daa5740f Mon Sep 17 00:00:00 2001 From: Francesco Fuggitti Date: Wed, 21 Jun 2023 14:34:34 -0400 Subject: [PATCH 1/2] fix: remove the TrueFormula and the FalseFormula to simplify the structure and fix the printing of empty Ands/Ors --- pddl/logic/base.py | 72 ++++------------------------------------------ 1 file changed, 5 insertions(+), 67 deletions(-) diff --git a/pddl/logic/base.py b/pddl/logic/base.py index 1b2c6448..5ff3e199 100644 --- a/pddl/logic/base.py +++ b/pddl/logic/base.py @@ -115,66 +115,6 @@ class Atomic(Formula): """Atomic formula.""" -class TrueFormula(Formula): - """A tautology.""" - - def __str__(self) -> str: - """Get the string representation.""" - return "(true)" - - def __repr__(self) -> str: - """Get an unambiguous string representation.""" - return "TrueFormula()" - - def __eq__(self, other): - """Compare with another object.""" - return isinstance(other, TrueFormula) - - def __hash__(self): - """Hash the object.""" - return hash(TrueFormula) - - def __invert__(self) -> Formula: - """Negate the formula.""" - return FALSE - - def __neg__(self) -> Formula: - """Negate.""" - return FALSE - - -class FalseFormula(Formula): - """A contradiction.""" - - def __str__(self) -> str: - """Get the string representation.""" - return "(false)" - - def __repr__(self) -> str: - """Get an unambiguous string representation.""" - return "FalseFormula()" - - def __eq__(self, other): - """Compare with another object.""" - return isinstance(other, FalseFormula) - - def __hash__(self): - """Hash the object.""" - return hash(FalseFormula) - - def __invert__(self) -> Formula: - """Negate the formula.""" - return TRUE - - def __neg__(self) -> Formula: - """Negate.""" - return TRUE - - -TRUE = TrueFormula() -FALSE = FalseFormula() - - class MonotoneOp(type): """Metaclass to simplify monotone operator instantiations.""" @@ -192,14 +132,14 @@ def __call__(cls, *args, **kwargs): class And(BinaryOp, metaclass=MonotoneOp): """And operator.""" - _absorbing = FALSE + _absorbing = False SYMBOL = "and" class Or(BinaryOp, metaclass=MonotoneOp): """Or operator.""" - _absorbing = TRUE + _absorbing = True SYMBOL = "or" @@ -296,10 +236,10 @@ def ensure_formula(f: Optional[Formula], is_none_true: bool) -> Formula: Ensure the argument is a formula. :param f: the formula, or None. - :param is_none_true: if true, None reduces to TrueFormula; FalseFormula otherwise. + :param is_none_true: if true, None reduces to And(); FalseFormula otherwise. :return: the same set, or an empty set if the arg was None. """ - return f if f is not None else TrueFormula() if is_none_true else FalseFormula() + return f if f is not None else And() if is_none_true else Or() def is_literal(formula: Formula) -> bool: @@ -323,11 +263,9 @@ def is_literal(formula: Formula) -> bool: def _simplify_monotone_op_operands(cls, *operands): operands = list(dict.fromkeys(operands)) if len(operands) == 0: - return [~cls._absorbing] + return [] elif len(operands) == 1: return [operands[0]] - elif cls._absorbing in operands: - return cls._absorbing # shift-up subformulas with same operator. DFS on expression tree. new_operands = [] From eac7882442b03a8accfafd380d152d0888f39c1f Mon Sep 17 00:00:00 2001 From: Francesco Fuggitti Date: Wed, 21 Jun 2023 14:36:24 -0400 Subject: [PATCH 2/2] update code and tests --- pddl/_validation.py | 16 +--------------- pddl/core.py | 4 ++-- pddl/formatter.py | 7 +------ pddl/parser/domain.py | 15 +++------------ tests/test_domain.py | 4 ++-- tests/test_formatter.py | 2 +- tests/test_problem.py | 4 ++-- 7 files changed, 12 insertions(+), 40 deletions(-) diff --git a/pddl/_validation.py b/pddl/_validation.py index 4f281bc2..d5f76fdc 100644 --- a/pddl/_validation.py +++ b/pddl/_validation.py @@ -22,13 +22,7 @@ from pddl.exceptions import PDDLValidationError from pddl.helpers.base import check, ensure, ensure_set, find_cycle from pddl.logic import Predicate -from pddl.logic.base import ( - BinaryOp, - FalseFormula, - QuantifiedCondition, - TrueFormula, - UnaryOp, -) +from pddl.logic.base import BinaryOp, QuantifiedCondition, UnaryOp from pddl.logic.effects import AndEffect, Forall, When from pddl.logic.predicates import DerivedPredicate, EqualTo from pddl.logic.terms import Term @@ -261,14 +255,6 @@ def _(self, formula: BinaryOp) -> None: """Check types annotations of a PDDL binary operator.""" self.check_type(formula.operands) - @check_type.register - def _(self, formula: TrueFormula) -> None: - """Check types annotations of a PDDL true formula.""" - - @check_type.register - def _(self, formula: FalseFormula) -> None: - """Check types annotations of a PDDL false formula.""" - @check_type.register def _(self, formula: QuantifiedCondition) -> None: """Check types annotations of a PDDL quantified condition.""" diff --git a/pddl/core.py b/pddl/core.py index 196d72b8..063c5182 100644 --- a/pddl/core.py +++ b/pddl/core.py @@ -27,7 +27,7 @@ from pddl.custom_types import name as name_type from pddl.custom_types import namelike, parse_name, to_names, to_types # noqa: F401 from pddl.helpers.base import assert_, check, ensure, ensure_set -from pddl.logic.base import Formula, TrueFormula, is_literal +from pddl.logic.base import And, Formula, is_literal from pddl.logic.predicates import DerivedPredicate, Predicate from pddl.logic.terms import Constant from pddl.requirements import Requirements @@ -172,7 +172,7 @@ def __init__( ] = self._parse_requirements(domain, requirements) self._objects: AbstractSet[Constant] = ensure_set(objects) self._init: AbstractSet[Formula] = ensure_set(init) - self._goal: Formula = ensure(goal, TrueFormula()) + self._goal: Formula = ensure(goal, And()) validate( all(map(is_literal, self.init)), "Not all formulas of initial condition are literals!", diff --git a/pddl/formatter.py b/pddl/formatter.py index 8b5a8454..acf47906 100644 --- a/pddl/formatter.py +++ b/pddl/formatter.py @@ -16,7 +16,6 @@ from pddl.core import Domain, Problem from pddl.custom_types import name -from pddl.logic.base import TRUE from pddl.logic.terms import Constant @@ -153,11 +152,7 @@ def problem_to_string(problem: Problem) -> str: body += _sort_and_print_collection( "(:init ", problem.init, ")\n", is_mandatory=True ) - body += ( - f"{'(:goal ' + str(problem.goal) + ')'}\n" - if problem.goal != TRUE - else "(:goal (and))\n" - ) + body += f"{'(:goal ' + str(problem.goal) + ')'}\n" result = result + "\n" + indent(body, indentation) + "\n)" result = _remove_empty_lines(result) return result diff --git a/pddl/parser/domain.py b/pddl/parser/domain.py index 2e7ebd1f..f0cf83bf 100644 --- a/pddl/parser/domain.py +++ b/pddl/parser/domain.py @@ -21,16 +21,7 @@ from pddl.custom_types import name from pddl.exceptions import PDDLMissingRequirementError, PDDLParsingError from pddl.helpers.base import assert_ -from pddl.logic.base import ( - And, - ExistsCondition, - FalseFormula, - ForallCondition, - Imply, - Not, - OneOf, - Or, -) +from pddl.logic.base import And, ExistsCondition, ForallCondition, Imply, Not, OneOf, Or from pddl.logic.effects import AndEffect, Forall, When from pddl.logic.predicates import DerivedPredicate, EqualTo, Predicate from pddl.logic.terms import Constant, Variable @@ -138,7 +129,7 @@ def action_parameters(self, args): def emptyor_pregd(self, args): """Process the 'emptyor_pregd' rule.""" if len(args) == 2: - return FalseFormula() + return Or() else: assert_(len(args) == 1) return args[0] @@ -217,7 +208,7 @@ def gd(self, args): def emptyor_effect(self, args): """Process the 'emptyor_effect' rule.""" if len(args) == 2: - return FalseFormula() + return Or() else: return args[0] diff --git a/tests/test_domain.py b/tests/test_domain.py index 08561d84..affbb618 100644 --- a/tests/test_domain.py +++ b/tests/test_domain.py @@ -21,7 +21,7 @@ from pddl.core import Domain from pddl.exceptions import PDDLValidationError from pddl.logic import Constant, Variable -from pddl.logic.base import Not, TrueFormula +from pddl.logic.base import And, Not from pddl.logic.helpers import constants, variables from pddl.logic.predicates import DerivedPredicate, Predicate from pddl.parser.symbols import Symbols @@ -157,7 +157,7 @@ def test_derived_predicate_type_not_available() -> None: """Test that when a type of a term of a derived predicate is not declared we raise error.""" x = Variable("a", type_tags={"t1", "t2"}) p = Predicate("p", x) - dp = DerivedPredicate(p, TrueFormula()) + dp = DerivedPredicate(p, And()) my_type = "my_type" type_set = {my_type: None} diff --git a/tests/test_formatter.py b/tests/test_formatter.py index 63bc66ec..52c40b3e 100644 --- a/tests/test_formatter.py +++ b/tests/test_formatter.py @@ -96,6 +96,6 @@ def test_typed_objects_formatting_in_problem() -> None: (:requirements :typing) (:objects a b c - type_1 d e f - type_2 g h i - type_3 j k l) (:init ) - (:goal (and)) + (:goal (and )) )""" ) diff --git a/tests/test_problem.py b/tests/test_problem.py index 2428506a..b858bde7 100644 --- a/tests/test_problem.py +++ b/tests/test_problem.py @@ -17,7 +17,7 @@ import pytest from pddl.core import Domain, Problem -from pddl.logic.base import Not, TrueFormula +from pddl.logic.base import And, Not from pddl.logic.helpers import constants, variables from pddl.logic.predicates import Predicate from tests.conftest import pddl_objects_problems @@ -64,7 +64,7 @@ def test_init(self): def test_goal(self): """Test the goal getter.""" - assert self.problem.goal == TrueFormula() + assert self.problem.goal == And() def test_build_simple_problem():