Skip to content

Commit

Permalink
fix(interpreted functions): mini-test for timed IF problem - cleaned …
Browse files Browse the repository at this point in the history
…comments and debug code
  • Loading branch information
Samuel Gobbi committed Sep 27, 2024
1 parent c2d9877 commit 6aadee4
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 139 deletions.
3 changes: 0 additions & 3 deletions unified_planning/engines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
)
from unified_planning.engines.oversubscription_planner import OversubscriptionPlanner
from unified_planning.engines.replanner import Replanner

# from unified_planning.engines.interpreted_functions_planner import InterpretedFunctionsPlanner
from unified_planning.engines.results import (
Result,
LogMessage,
Expand All @@ -53,7 +51,6 @@
from unified_planning.engines.mixins.compiler import CompilationKind
from unified_planning.engines.mixins.portfolio import PortfolioSelectorMixin


__all__ = [
"Factory",
"Grounder",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# copyright info is not up to date as of september 27th 2024
"""This module defines the interpreted functions effects remover class."""


Expand Down Expand Up @@ -70,8 +71,8 @@ def __init__(self):
def name(self):
return "ifrm"

@staticmethod # change this # change this
def supported_kind() -> ProblemKind: # change this # change this
@staticmethod
def supported_kind() -> ProblemKind:
supported_kind = ProblemKind(version=LATEST_PROBLEM_KIND_VERSION)
supported_kind.set_problem_class("ACTION_BASED")
supported_kind.set_typing("FLAT_TYPING")
Expand Down Expand Up @@ -133,8 +134,8 @@ def supported_kind() -> ProblemKind: # change this # change this
supported_kind.set_quality_metrics("FINAL_VALUE")
supported_kind.set_actions_cost_kind("INT_NUMBERS_IN_ACTIONS_COST")
supported_kind.set_actions_cost_kind("REAL_NUMBERS_IN_ACTIONS_COST")
supported_kind.set_oversubscription_kind("INT_NUMBERS_IN_OVERSUBSCRIPTION")
supported_kind.set_oversubscription_kind("REAL_NUMBERS_IN_OVERSUBSCRIPTION")
# supported_kind.set_oversubscription_kind("INT_NUMBERS_IN_OVERSUBSCRIPTION")
# supported_kind.set_oversubscription_kind("REAL_NUMBERS_IN_OVERSUBSCRIPTION")
return supported_kind

@staticmethod
Expand All @@ -146,6 +147,7 @@ def supports_compilation(compilation_kind: CompilationKind) -> bool:
return compilation_kind == CompilationKind.INTERPRETED_FUNCTIONS_REMOVING

def _fix_precondition(self, a):
# should we check for always true preconditions?
# simplified_precondition = simplifier.simplify(p)
# precondition_operators = operators_extractor.get(simplified_precondition)
# operators_extractor: up.model.walkers.OperatorsExtractor = (
Expand All @@ -157,7 +159,6 @@ def _fix_precondition(self, a):
templist.append(sub)
else:
templist.append(a)
# print (templist)
return templist

@staticmethod
Expand Down Expand Up @@ -192,11 +193,6 @@ def _compile(
simplifier = env.simplifier

new_to_old: Dict[Action, Optional[Action]] = {}
# name_action_map: Dict[str, Union[InstantaneousAction, DurativeAction]] = {}

# operators_extractor: up.model.walkers.OperatorsExtractor = (
# up.model.walkers.OperatorsExtractor()
# )
new_problem = problem.clone()
new_problem.name = f"{self.name}_{problem.name}"
new_problem.clear_actions()
Expand All @@ -210,9 +206,6 @@ def _compile(
for p in a.preconditions:
templist = self._fix_precondition(p)
fixed_preconditions.extend(templist)
# print ("\n\n fixed preconds \n\n")
# print (fixed_preconditions)
# print (a.preconditions)
for p in fixed_preconditions:
precondition_operators = self.operators_extractor.get(p)

Expand Down Expand Up @@ -254,15 +247,6 @@ def _compile(
raise NotImplementedError

new_problem.add_action(no_IF_action)
# no_IF_action = a.clone()
# no_IF_action.clear_preconditions()
# for p in a.preconditions.items():
# #simplified_precondition = simplifier.simplify(p)
# #precondition_operators = operators_extractor.get(simplified_precondition)
# precondition_operators = operators_extractor.get(p)
# if not(OperatorKind.INTERPRETED_FUNCTION_EXP in precondition_operators):
# no_IF_action.add_precondition(p)
# new_problem.add_action(no_IF_action)

return CompilerResult(
new_problem, partial(replace_action, map=new_to_old), self.name
Expand Down
18 changes: 11 additions & 7 deletions unified_planning/engines/interpreted_functions_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# copyright info is not up to date as of september 27th 2024

import time
import unified_planning as up
Expand All @@ -30,6 +31,7 @@
ValidationResultStatus,
)
from unified_planning.engines.mixins.oneshot_planner import OptimalityGuarantee
from unified_planning.plans.time_triggered_plan import TimeTriggeredPlan
from unified_planning.shortcuts import Compiler
from unified_planning.utils import powerset
from typing import Type, IO, Optional, Union, List, Tuple, Callable
Expand Down Expand Up @@ -153,8 +155,6 @@ def _attempt_to_solve(
output_stream: Optional[IO[str]] = None,
) -> "PlanGenerationResult":
new_problem = compilerresult.problem
# print (new_problem)
# print (new_problem.kind)

start = time.time()

Expand All @@ -172,25 +172,29 @@ def _attempt_to_solve(
self.name,
log_messages=res.log_messages,
)
elif isinstance(
res.plan, TimeTriggeredPlan
): # we currently cannot check the validity of time triggered plans
return PlanGenerationResult(
PlanGenerationResultStatus.UNSUPPORTED_PROBLEM,
res.plan,
self.name,
)

mapback = compilerresult.map_back_action_instance
mappedbackplan = res.plan.replace_action_instances(mapback)
# print (res.plan)

spv = SequentialPlanValidator(environment=get_environment())
spv.skip_checks = True # --------------------------------------------------------------------------
validation_result = spv.validate(problem, mappedbackplan)
if validation_result.status == ValidationResultStatus.VALID:
# print("the plan is ok")
retval = PlanGenerationResult(
status,
mappedbackplan,
self.name,
log_messages=res.log_messages,
)
else:
# print ("validation error")
# print (validation_result)
# print (validation_result.status)
retval = _refine(
PlanGenerationResultStatus.INTERNAL_ERROR,
up.plans.SequentialPlan([]),
Expand Down
1 change: 0 additions & 1 deletion unified_planning/engines/parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ def supports_plan(plan_kind: "up.plans.PlanKind") -> bool:
def _run_parallel(self, fname, *args) -> List[Result]:
signaling_queue: Queue = Queue()
processes = []

for idx, (engine_name, opts) in enumerate(self.engines):
options = opts
_p = Process(
Expand Down
4 changes: 1 addition & 3 deletions unified_planning/engines/sequential_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,9 +620,7 @@ def supported_kind() -> "up.model.ProblemKind":
supported_kind.set_conditions_kind("EQUALITIES")
supported_kind.set_conditions_kind("EXISTENTIAL_CONDITIONS")
supported_kind.set_conditions_kind("UNIVERSAL_CONDITIONS")
supported_kind.set_conditions_kind(
"INTERPRETED_FUNCTIONS_IN_CONDITIONS"
) # causes problems in test_partial_order_plan
supported_kind.set_conditions_kind("INTERPRETED_FUNCTIONS_IN_CONDITIONS")
supported_kind.set_effects_kind("CONDITIONAL_EFFECTS")
supported_kind.set_effects_kind("INCREASE_EFFECTS")
supported_kind.set_effects_kind("DECREASE_EFFECTS")
Expand Down
11 changes: 2 additions & 9 deletions unified_planning/model/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,8 @@
bool,
]
NumericConstant = Union[int, float, Fraction, str]
NumericExpression = Union[
NumericConstant,
"up.model.fnode.FNode",
]
ConstantExpression = Union[
NumericExpression,
"up.model.object.Object",
bool,
]
NumericExpression = Union[NumericConstant, "up.model.fnode.FNode"]
ConstantExpression = Union[NumericExpression, "up.model.object.Object", bool]
TimeExpression = Union[
"up.model.timing.Timing",
"up.model.timing.Timepoint",
Expand Down
14 changes: 1 addition & 13 deletions unified_planning/model/interpreted_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# copyright info is not up to date as of september 27th 2024
"""
This module defines the InterpretedFunction class.
An InterpretedFunction has a name, a return type, a signature
Expand Down Expand Up @@ -60,19 +61,6 @@ def __init__(
up.model.parameter.Parameter(param_name, param_type, self._env)
)

# for param in self._signature:
# pt = param.type
# if pt.is_real_type() or (
# pt.is_int_type()
# and (
# cast(_IntType, pt).lower_bound is None
# or cast(_IntType, pt).upper_bound is None
# )
# ):
# raise UPTypeError(
# f"Parameter {param} of interpreted function {name} has type {pt}; interpreted function parameters must be int|real|bool|userType." # this should probably be different
# )

def __repr__(self) -> str:
sign = ""
if self.arity > 0:
Expand Down
4 changes: 0 additions & 4 deletions unified_planning/model/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ def __init__(
FluentsSetMixin.__init__(
self, self.environment, self._add_user_type, self.has_name, initial_defaults
)

ActionsSetMixin.__init__(
self, self.environment, self._add_user_type, self.has_name
)
Expand Down Expand Up @@ -778,7 +777,6 @@ def update_problem_kind_effect(
self,
e: "up.model.effect.Effect",
):
# value = self.simplifier.simplify(e.value)
value = e.value
fluents_in_value = self.environment.free_vars_extractor.get(value)
ops = self.operators_extractor.get(value)
Expand Down Expand Up @@ -808,7 +806,6 @@ def update_problem_kind_effect(
self.kind.unset_problem_type("SIMPLE_NUMERIC_PLANNING")
else:
self.kind.unset_problem_type("SIMPLE_NUMERIC_PLANNING")

if any(f in self.static_fluents for f in fluents_in_value):
self.kind.set_effects_kind("STATIC_FLUENTS_IN_NUMERIC_ASSIGNMENTS")
if any(f not in self.static_fluents for f in fluents_in_value):
Expand Down Expand Up @@ -836,7 +833,6 @@ def update_problem_kind_effect(
if any(f not in self.static_fluents for f in fluents_in_value):
self.kind.set_effects_kind("FLUENTS_IN_NUMERIC_ASSIGNMENTS")
elif e.is_assignment():

value_type = value.type
if (
value_type.is_int_type() or value_type.is_real_type()
Expand Down
2 changes: 1 addition & 1 deletion unified_planning/test/pddl/counters/problem3.pddl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
;; edit of the original example by Enrico Scala (enricos83@gmail.com) and Miquel Ramirez (miquel.ramirez@gmail.com)
;; to make tests faster - this still verifies anytime requirements in unified_planning as of septemer 27th 2024
;; the bloat of unused counters still makes it that the first solution is not optimal, but takes less time to run tests
(define (problem instance_12_reduced)
(define (problem instance_12_mod)
(:domain fn-counters)
(:objects
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 - counter
Expand Down
52 changes: 48 additions & 4 deletions unified_planning/test/test_anytime.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class TestAnytimePlanning(unittest_TestCase):
simple_numeric_kind.union(quality_metrics_kind),
up.engines.AnytimeGuarantee.INCREASING_QUALITY,
)
def test_counters(self):
def test_counters_modified(self):
reader = PDDLReader()
domain_filename = os.path.join(PDDL_DOMAINS_PATH, "counters", "domain.pddl")
problem_filename = os.path.join(PDDL_DOMAINS_PATH, "counters", "problem3.pddl")
Expand All @@ -58,16 +58,60 @@ def test_counters(self):
solutions.append(p)
if len(solutions) == 2:
break
# print(solutions[0].actions)
# print(solutions[1].actions)

self.assertEqual(len(solutions), 2)
if solutions[1].status == PlanGenerationResultStatus.INTERMEDIATE:
self.assertGreater(
len(solutions[0].plan.actions), len(solutions[1].plan.actions)
)
elif solutions[1].status == PlanGenerationResultStatus.SOLVED_SATISFICING:
self.assertGreaterEqual(
self.assertEqual(
len(solutions[0].plan.actions), len(solutions[1].plan.actions)
)
elif solutions[1].status == PlanGenerationResultStatus.INTERNAL_ERROR:
print("this sometimes happens with large problems")
print(solutions)
self.assertGreater(
len(solutions[0].plan.actions), len(solutions[1].plan.actions)
)
# this will most likely error if you somehow got here
else:
print(solutions)
self.assertGreater(
len(solutions[0].plan.actions), len(solutions[1].plan.actions)
)
# this will most likely error if you somehow got here

@skipIfNoAnytimePlannerForProblemKind(
simple_numeric_kind.union(quality_metrics_kind),
up.engines.AnytimeGuarantee.INCREASING_QUALITY,
)
def test_counters_small(self):
reader = PDDLReader()
domain_filename = os.path.join(PDDL_DOMAINS_PATH, "counters", "domain.pddl")
problem_filename = os.path.join(PDDL_DOMAINS_PATH, "counters", "problem.pddl")
# using the modified problem3 to save time
problem = reader.parse_problem(domain_filename, problem_filename)
problem.add_quality_metric(MinimizeSequentialPlanLength())

with AnytimePlanner(
problem_kind=problem.kind, anytime_guarantee="INCREASING_QUALITY"
) as planner:
self.assertTrue(planner.is_anytime_planner())
solutions = []
for p in planner.get_solutions(problem):
self.assertTrue(p.plan is not None)
solutions.append(p)
if len(solutions) == 2:
break

self.assertEqual(len(solutions), 2)
if solutions[1].status == PlanGenerationResultStatus.INTERMEDIATE:
self.assertGreater(
len(solutions[0].plan.actions), len(solutions[1].plan.actions)
)
elif solutions[1].status == PlanGenerationResultStatus.SOLVED_SATISFICING:
self.assertEqual(
len(solutions[0].plan.actions), len(solutions[1].plan.actions)
)
elif solutions[1].status == PlanGenerationResultStatus.INTERNAL_ERROR:
Expand Down
15 changes: 6 additions & 9 deletions unified_planning/test/test_interpreted_functions_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import pytest
import unified_planning
from unified_planning.engines.results import PlanGenerationResultStatus
from unified_planning.shortcuts import *
from unified_planning.exceptions import UPProblemDefinitionError
from unified_planning.model import GlobalStartTiming
Expand Down Expand Up @@ -101,23 +102,19 @@ def test_interpreted_functions_in_preconditions_planner_refine(self):

@skipIfEngineNotAvailable("sequential_plan_validator")
@skipIfEngineNotAvailable("tamer")
@pytest.mark.skip(reason="Tamer returns TimeTriggeredPlan instad of Sequential")
# @pytest.mark.skip(reason="Tamer returns TimeTriggeredPlan instad of Sequential")
def test_interpreted_functions_in_durations_planner(self):
problem = self.problems["interpreted_functions_in_durations"].problem
# print (problem)
# print (problem.kind)
pa = problem.action("gohome")
self.assertTrue(problem.kind.has_interpreted_functions_in_durations())

with OneshotPlanner(name="interpreted_functions_planning[tamer]") as planner:
print("now attempting to solve")
result = planner.solve(problem)
print(result)
print(result.plan)

self.assertTrue(result.status in up.engines.results.POSITIVE_OUTCOMES)
self.assertTrue(result.status == PlanGenerationResultStatus.UNSUPPORTED_PROBLEM)
self.assertEqual(len(result.plan._actions), 1)
self.assertTrue(
result.plan.__eq__(
self.problems["interpreted_functions_in_conditions"].valid_plans[0]
)
)
self.assertEqual((result.plan.timed_actions[0])[1].action.name, pa.name)
# the action objects are different, one contains an IF, the other has 1-1000000 as time instead
Loading

0 comments on commit 6aadee4

Please sign in to comment.