From aec63e57a95798e2fbc064769da2ce7e91c5702d Mon Sep 17 00:00:00 2001 From: ssardina Date: Wed, 14 Feb 2024 16:45:59 +1100 Subject: [PATCH 1/6] implement __str__ for Domain and Problem --- pddl/core.py | 50 +++++++++++++++++++++++++++++++++++ pddl/formatter.py | 66 ++++++++++------------------------------------- 2 files changed, 63 insertions(+), 53 deletions(-) diff --git a/pddl/core.py b/pddl/core.py index d60f954..77a45a0 100644 --- a/pddl/core.py +++ b/pddl/core.py @@ -34,6 +34,8 @@ from pddl.logic.terms import Constant from pddl.requirements import Requirements +from pddl.formatter import sort_and_print_collection, print_types_or_functions_with_parents, print_predicates_with_types, remove_empty_lines, print_constants, print_function_skeleton, indent + class Domain: """A class for a PDDL domain file.""" @@ -148,6 +150,36 @@ def __eq__(self, other): and self.actions == other.actions ) + def __str__(self) -> str: + """Print a PDDL domain object.""" + result = f"(define (domain {self.name})" + body = "" + indentation = " " * 4 + body += sort_and_print_collection("(:requirements ", self.requirements, ")\n") + body += print_types_or_functions_with_parents("(:types", self.types, ")\n") + body += print_constants("(:constants", self.constants, ")\n") + if self.predicates: + body += f"(:predicates {print_predicates_with_types(self.predicates)})\n" + if self.functions: + body += print_types_or_functions_with_parents( + "(:functions", self.functions, ")\n", print_function_skeleton + ) + body += sort_and_print_collection( + "", + self.derived_predicates, + "", + to_string=lambda obj: str(obj) + "\n", + ) + body += sort_and_print_collection( + "", + self.actions, + "", + to_string=lambda obj: str(obj) + "\n", + ) + result = result + "\n" + indent(body, indentation) + "\n)" + result = remove_empty_lines(result) + return result + class Problem: """A class for a PDDL problem file.""" @@ -331,3 +363,21 @@ def __eq__(self, other): and self.goal == other.goal and self.metric == other.metric ) + + def __str__(self) -> str: + """Print a PDDL problem object.""" + result = f"(define (problem {self.name})" + body = f"(:domain {self.domain_name})\n" + indentation = " " * 4 + body += sort_and_print_collection("(:requirements ", self.requirements, ")\n") + if self.objects: + body += print_constants("(:objects", self.objects, ")\n") + body += sort_and_print_collection( + "(:init ", self.init, ")\n", is_mandatory=True + ) + body += f"{'(:goal ' + str(self.goal) + ')'}\n" + body += f"{'(:metric ' + str(self.metric) + ')'}\n" if self.metric else "" + result = result + "\n" + indent(body, indentation) + "\n)" + result = remove_empty_lines(result) + return result + diff --git a/pddl/formatter.py b/pddl/formatter.py index 7c1edcf..5283d14 100644 --- a/pddl/formatter.py +++ b/pddl/formatter.py @@ -14,7 +14,6 @@ from textwrap import indent from typing import Callable, Collection, Dict, List, Optional, TypeVar -from pddl.core import Domain, Problem from pddl.custom_types import name from pddl.logic.functions import NumericFunction from pddl.logic.terms import Constant @@ -22,12 +21,12 @@ T = TypeVar("T", name, NumericFunction) -def _remove_empty_lines(s: str) -> str: +def remove_empty_lines(s: str) -> str: """Remove empty lines from string.""" return "\n".join(filter(str.strip, s.splitlines())) -def _sort_and_print_collection( +def sort_and_print_collection( prefix, collection: Collection, postfix, @@ -41,7 +40,7 @@ def _sort_and_print_collection( return "" -def _print_types_or_functions_with_parents( +def print_types_or_functions_with_parents( prefix: str, types_dict: Dict[T, Optional[name]], postfix: str, @@ -53,10 +52,10 @@ def _print_types_or_functions_with_parents( name_by_obj.setdefault(parent_type, []).append(obj_name) # type: ignore if not bool(name_by_obj): return "" - return _print_typed_lists(prefix, name_by_obj, postfix, to_string) + return print_typed_lists(prefix, name_by_obj, postfix, to_string) -def _print_constants( +def print_constants( prefix, constants: Collection[Constant], postfix, to_string: Callable = str ): """Print constants in a PDDL domain.""" @@ -65,10 +64,10 @@ def _print_constants( term_by_type_tags.setdefault(c.type_tag, []).append(c.name) if not bool(term_by_type_tags): return "" - return _print_typed_lists(prefix, term_by_type_tags, postfix, to_string) + return print_typed_lists(prefix, term_by_type_tags, postfix, to_string) -def _print_predicates_with_types(predicates: Collection): +def print_predicates_with_types(predicates: Collection): result = "" for p in sorted(predicates): if p.arity == 0: @@ -89,7 +88,7 @@ def _print_predicates_with_types(predicates: Collection): return result.strip() -def _print_function_skeleton(function: NumericFunction) -> str: +def print_function_skeleton(function: NumericFunction) -> str: """Callable to print a function skeleton with type tags.""" result = "" if function.arity == 0: @@ -106,7 +105,7 @@ def _print_function_skeleton(function: NumericFunction) -> str: return result -def _print_typed_lists( +def print_typed_lists( prefix, names_by_obj: Dict[Optional[T], List[name]], postfix, @@ -139,50 +138,11 @@ def _print_typed_lists( return result -def domain_to_string(domain: Domain) -> str: +def domain_to_string(domain) -> str: """Print a PDDL domain object.""" - result = f"(define (domain {domain.name})" - body = "" - indentation = " " * 4 - body += _sort_and_print_collection("(:requirements ", domain.requirements, ")\n") - body += _print_types_or_functions_with_parents("(:types", domain.types, ")\n") - body += _print_constants("(:constants", domain.constants, ")\n") - if domain.predicates: - body += f"(:predicates {_print_predicates_with_types(domain.predicates)})\n" - if domain.functions: - body += _print_types_or_functions_with_parents( - "(:functions", domain.functions, ")\n", _print_function_skeleton - ) - body += _sort_and_print_collection( - "", - 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) - return result + return str(domain) -def problem_to_string(problem: Problem) -> str: +def problem_to_string(problem) -> str: """Print a PDDL problem object.""" - result = f"(define (problem {problem.name})" - body = f"(:domain {problem.domain_name})\n" - indentation = " " * 4 - body += _sort_and_print_collection("(:requirements ", problem.requirements, ")\n") - if problem.objects: - body += _print_constants("(:objects", problem.objects, ")\n") - body += _sort_and_print_collection( - "(:init ", problem.init, ")\n", is_mandatory=True - ) - body += f"{'(:goal ' + str(problem.goal) + ')'}\n" - body += f"{'(:metric ' + str(problem.metric) + ')'}\n" if problem.metric else "" - result = result + "\n" + indent(body, indentation) + "\n)" - result = _remove_empty_lines(result) - return result + return str(problem) \ No newline at end of file From e788bbe940b63ca158e55ef831e4af406049761a Mon Sep 17 00:00:00 2001 From: ssardina Date: Sat, 16 Mar 2024 15:39:28 +1100 Subject: [PATCH 2/6] fix: source file formatting --- pddl/core.py | 18 ++++++++++++------ pddl/formatter.py | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pddl/core.py b/pddl/core.py index 77a45a0..5cea66d 100644 --- a/pddl/core.py +++ b/pddl/core.py @@ -9,7 +9,6 @@ # license that can be found in the LICENSE file or at # https://opensource.org/licenses/MIT. # - """ Core module of the package. @@ -34,7 +33,15 @@ from pddl.logic.terms import Constant from pddl.requirements import Requirements -from pddl.formatter import sort_and_print_collection, print_types_or_functions_with_parents, print_predicates_with_types, remove_empty_lines, print_constants, print_function_skeleton, indent +from pddl.formatter import ( + sort_and_print_collection, + print_types_or_functions_with_parents, + print_predicates_with_types, + remove_empty_lines, + print_constants, + print_function_skeleton, + indent, +) class Domain: @@ -213,9 +220,9 @@ def __init__( self._domain, self._domain_name = self._parse_domain_and_domain_name( domain, domain_name ) - self._requirements: Optional[ - AbstractSet[Requirements] - ] = self._parse_requirements(domain, requirements) + self._requirements: Optional[AbstractSet[Requirements]] = ( + self._parse_requirements(domain, requirements) + ) self._objects: AbstractSet[Constant] = ensure_set(objects) self._init: AbstractSet[Formula] = ensure_set(init) self._goal: Formula = ensure(goal, And()) @@ -380,4 +387,3 @@ def __str__(self) -> str: result = result + "\n" + indent(body, indentation) + "\n)" result = remove_empty_lines(result) return result - diff --git a/pddl/formatter.py b/pddl/formatter.py index 5283d14..483778f 100644 --- a/pddl/formatter.py +++ b/pddl/formatter.py @@ -145,4 +145,4 @@ def domain_to_string(domain) -> str: def problem_to_string(problem) -> str: """Print a PDDL problem object.""" - return str(problem) \ No newline at end of file + return str(problem) From ca3612e70cae49a168b3be7147a3c25f1e8fe3c1 Mon Sep 17 00:00:00 2001 From: ssardina Date: Tue, 19 Mar 2024 09:30:33 +1100 Subject: [PATCH 3/6] fix: apply black 23.3 to file core.py --- pddl/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pddl/core.py b/pddl/core.py index 5cea66d..1ce13eb 100644 --- a/pddl/core.py +++ b/pddl/core.py @@ -220,9 +220,9 @@ def __init__( self._domain, self._domain_name = self._parse_domain_and_domain_name( domain, domain_name ) - self._requirements: Optional[AbstractSet[Requirements]] = ( - self._parse_requirements(domain, requirements) - ) + self._requirements: Optional[ + AbstractSet[Requirements] + ] = self._parse_requirements(domain, requirements) self._objects: AbstractSet[Constant] = ensure_set(objects) self._init: AbstractSet[Formula] = ensure_set(init) self._goal: Formula = ensure(goal, And()) From eeb12f95da108d580e7fe7ca1d834345bc9e0fe1 Mon Sep 17 00:00:00 2001 From: ssardina Date: Wed, 20 Mar 2024 10:02:10 +1100 Subject: [PATCH 4/6] fix: address formatting and style issues --- pddl/core.py | 25 ++++++++++++------------- pddl/formatter.py | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/pddl/core.py b/pddl/core.py index 1ce13eb..8c27509 100644 --- a/pddl/core.py +++ b/pddl/core.py @@ -14,6 +14,7 @@ It contains the class definitions to build and modify PDDL domains or problems. """ +from textwrap import indent from typing import AbstractSet, Collection, Dict, Optional, Tuple, cast from pddl._validation import ( @@ -26,6 +27,14 @@ from pddl.action import Action 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.formatter import ( + print_constants, + print_function_skeleton, + print_predicates_with_types, + print_types_or_functions_with_parents, + remove_empty_lines, + sort_and_print_collection, +) from pddl.helpers.base import assert_, check, ensure, ensure_set from pddl.logic.base import And, Formula, is_literal from pddl.logic.functions import FunctionExpression, Metric, NumericFunction @@ -33,16 +42,6 @@ from pddl.logic.terms import Constant from pddl.requirements import Requirements -from pddl.formatter import ( - sort_and_print_collection, - print_types_or_functions_with_parents, - print_predicates_with_types, - remove_empty_lines, - print_constants, - print_function_skeleton, - indent, -) - class Domain: """A class for a PDDL domain file.""" @@ -220,9 +219,9 @@ def __init__( self._domain, self._domain_name = self._parse_domain_and_domain_name( domain, domain_name ) - self._requirements: Optional[ - AbstractSet[Requirements] - ] = self._parse_requirements(domain, requirements) + self._requirements: Optional[AbstractSet[Requirements]] = ( + self._parse_requirements(domain, requirements) + ) self._objects: AbstractSet[Constant] = ensure_set(objects) self._init: AbstractSet[Formula] = ensure_set(init) self._goal: Formula = ensure(goal, And()) diff --git a/pddl/formatter.py b/pddl/formatter.py index 483778f..3655417 100644 --- a/pddl/formatter.py +++ b/pddl/formatter.py @@ -11,7 +11,6 @@ # """Formatting utilities for PDDL domains and problems.""" -from textwrap import indent from typing import Callable, Collection, Dict, List, Optional, TypeVar from pddl.custom_types import name @@ -33,6 +32,20 @@ def sort_and_print_collection( to_string: Callable = str, is_mandatory: bool = False, ): + """Given a collection (requirements, actions, objects, etc.) produced its sorted string version with prefix and postfix strings. + Prefix is used to start with strings like "(:requirements" or "(:actions" + Postfix is used to close the structure, usually with ")\n" + + Args: + prefix (str): start of the string + collection (Collection): the collection of entities to report as a string + postfix (str): the end of the string + to_string (Callable, optional): the function to use to convert to string. Defaults to str. + is_mandatory (bool, optional): if the string is mandatory even if the collection is empty. Defaults to False. + + Returns: + str: a string with + """ if len(collection) > 0: return prefix + " ".join(sorted(map(to_string, collection))) + postfix elif is_mandatory: From 7028b10d9f11ca28384e4091a6151de4ec7903ae Mon Sep 17 00:00:00 2001 From: ssardina Date: Thu, 21 Mar 2024 07:43:51 +1100 Subject: [PATCH 5/6] fix: black formatting on core.py --- pddl/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pddl/core.py b/pddl/core.py index 8c27509..984289c 100644 --- a/pddl/core.py +++ b/pddl/core.py @@ -219,9 +219,9 @@ def __init__( self._domain, self._domain_name = self._parse_domain_and_domain_name( domain, domain_name ) - self._requirements: Optional[AbstractSet[Requirements]] = ( - self._parse_requirements(domain, requirements) - ) + self._requirements: Optional[ + AbstractSet[Requirements] + ] = self._parse_requirements(domain, requirements) self._objects: AbstractSet[Constant] = ensure_set(objects) self._init: AbstractSet[Formula] = ensure_set(init) self._goal: Formula = ensure(goal, And()) From 553de647c8f6431bfe4e0f080f03a22c01524807 Mon Sep 17 00:00:00 2001 From: ssardina Date: Thu, 21 Mar 2024 08:19:41 +1100 Subject: [PATCH 6/6] fix: formatting issues; pass flake8 checks --- pddl/formatter.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pddl/formatter.py b/pddl/formatter.py index 3655417..5b63344 100644 --- a/pddl/formatter.py +++ b/pddl/formatter.py @@ -32,9 +32,11 @@ def sort_and_print_collection( to_string: Callable = str, is_mandatory: bool = False, ): - """Given a collection (requirements, actions, objects, etc.) produced its sorted string version with prefix and postfix strings. - Prefix is used to start with strings like "(:requirements" or "(:actions" - Postfix is used to close the structure, usually with ")\n" + r"""Produce the string of a PDDL section for a collection (e.g., requirements, actions, objects, etc.). + + Prefix starts the PDDL section, like "(:requirements" or "(:actions" + Postfix ends the section, usually with ")\n" + The collection is sorted and printed as a string, using to_string to convert each element to a string. Args: prefix (str): start of the string @@ -81,6 +83,7 @@ def print_constants( def print_predicates_with_types(predicates: Collection): + """Generate a string with predicates with type tags for the :predicates section.""" result = "" for p in sorted(predicates): if p.arity == 0: