# Copyright 2021-2023 AIPlan4EU project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import unified_planning as up
from unified_planning.model import ProblemKind
from abc import ABC, abstractmethod
from enum import Enum, auto
from typing import Optional
from warnings import warn
[docs]
class CompilationKind(Enum):
"""Enum representing the available compilation kinds currently in the library."""
GROUNDING = auto()
CONDITIONAL_EFFECTS_REMOVING = auto()
DISJUNCTIVE_CONDITIONS_REMOVING = auto()
NEGATIVE_CONDITIONS_REMOVING = auto()
QUANTIFIERS_REMOVING = auto()
TRAJECTORY_CONSTRAINTS_REMOVING = auto()
USERTYPE_FLUENTS_REMOVING = auto()
BOUNDED_TYPES_REMOVING = auto()
STATE_INVARIANTS_REMOVING = auto()
MA_SINGLE_AGENT_PROJECTION = auto()
MA_CENTRALIZATION = auto()
MA_SL_ROBUSTNESS_VERIFICATION = auto()
MA_SL_SOCIAL_LAW = auto()
SA_MA_CONVERSION = auto()
TIMED_TO_SEQUENTIAL = auto()
DURATIVE_ACTIONS_TO_PROCESSES = auto()
UNDEFINED_INITIAL_NUMERIC_REMOVING = auto()
class CompilerMixin(ABC):
"""Generic class for a compiler defining it's interface."""
def __init__(self, default: Optional[CompilationKind] = None):
self._default = default
def compile(
self,
problem: "up.model.AbstractProblem",
compilation_kind: Optional[CompilationKind] = None,
) -> "up.engines.results.CompilerResult":
"""
Takes an instance of an `AbstractProblem` and a supported
`CompilationKind` and returns the generated `CompilerResult`; a data structure
containing the compiled `AbstractProblem`, a function that allows the rewriting
of a :class:`~unified_planning.plans.Plan` generated for the compiled `AbstractProblem` to a `Plan`
for the original `AbstractProblem` and some compiler info, like the name and some logs on the compiling.
If the `compilation_kind` is not specified, the `default` is used.
For more information about the `CompilerResult` returned, read the class documentation
above.
:param problem: The instance of the `AbstractProblem` on which the compilation is applied.
:param compilation_kind: The `CompilationKind` that must be applied on the given problem.
:return: The resulting `CompilerResult`.
:raises: :exc:`~unified_planning.exceptions.UPUsageError` if the given `compilation_kind` is None and the
:func:`default<unified_planning.engines.mixins.CompilerMixin.default>` is None or
if the given `compilation_kind` is not supported by the
:func:`~unified_planning.engines.mixins.CompilerMixin.supports_compilation` method or
if the :func:`problem_kind <unified_planning.model.Problem.kind>` is not supported by the
:func:`~unified_planning.engines.Engine.supports` method.
"""
assert isinstance(self, up.engines.engine.Engine)
if compilation_kind is None:
compilation_kind = self._default
if compilation_kind is None:
raise up.exceptions.UPUsageError(f"Compilation kind needs to be specified!")
if not self.skip_checks and not self.supports(problem.kind):
msg = f"We cannot establish whether {self.name} can handle this problem!"
if self.error_on_failed_checks:
raise up.exceptions.UPUsageError(msg)
else:
warn(msg)
if not self.supports_compilation(compilation_kind):
msg = f"{self.name} cannot handle this kind of compilation!"
if self.error_on_failed_checks:
raise up.exceptions.UPUsageError(msg)
else:
warn(msg)
return self._compile(problem, compilation_kind)
@property
def default(self) -> Optional[CompilationKind]:
"""
Returns the default compilation kind for this compiler.
When a compiler is returned with the :meth:`~unified_planning.engines.Factory.Compiler` operation mode
with a `CompilationKind` as parameter, the given `CompilationKind` is set as the `default`.
:return: The `CompilationKind` set as default.
"""
return self._default
@default.setter
def default(self, default: Optional[CompilationKind]):
"""
Sets the default compilation kind.
:default: The default compilation kind to set.
"""
self._default = default
@staticmethod
def is_compiler() -> bool:
"""Returns True."""
return True
@staticmethod
@abstractmethod
def supports_compilation(compilation_kind: CompilationKind) -> bool:
"""
:param compilation_kind: The tested `CompilationKind`.
:return: True if the given `CompilationKind` is supported
by this compiler, False otherwise.
"""
raise NotImplementedError
@staticmethod
@abstractmethod
def resulting_problem_kind(
problem_kind: ProblemKind, compilation_kind: Optional[CompilationKind] = None
) -> ProblemKind:
"""
Returns the `ProblemKind` of an :class:`~unified_planning.model.AbstractProblem` which is returned by the
:meth:`~unified_planning.engines.mixins.compiler.CompilerMixin.compile` method with the given `CompilationKind`.
:param problem_kind: The given `ProblemKind`.
:param compilation_kind: The `CompilationKind` applied to modify the `ProblemKind`.
:return: The resulting `ProblemKind`.
"""
raise NotImplementedError
@abstractmethod
def _compile(
self, problem: "up.model.AbstractProblem", compilation_kind: CompilationKind
) -> "up.engines.results.CompilerResult":
"""Method called by :func:`~unified_planning.engines.mixins.CompilerMixin.compile` to get the returned :class:`~unified_planning.engines.CompilerResult`."""
raise NotImplementedError