Skip to content

Commit

Permalink
feature(interpreted functions): effects compiler basic functionalitie…
Browse files Browse the repository at this point in the history
…s - untested
  • Loading branch information
Samuel Gobbi committed Nov 4, 2024
1 parent ae606d9 commit ef06156
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 64 deletions.
147 changes: 95 additions & 52 deletions unified_planning/engines/compilers/interpreted_functions_remover.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from unified_planning.model.fluent import Fluent
from unified_planning.model.mixins.timed_conds_effs import TimedCondsEffs
from unified_planning.model.object import Object
from unified_planning.model.effect import Effect
from unified_planning.model.problem_kind_versioning import LATEST_PROBLEM_KIND_VERSION
from unified_planning.engines.compilers.utils import (
get_fresh_name,
Expand Down Expand Up @@ -64,6 +65,9 @@ def __init__(self, interpreted_functions_values=None):
self.interpreted_functions_extractor: up.model.walkers.InterpretedFunctionsExtractor = (
up.model.walkers.InterpretedFunctionsExtractor()
)
self.names_extractor: up.model.walkers.NamesExtractor = (
up.model.walkers.NamesExtractor()
)
self._use_old_algorithm = False
self._interpreted_functions_values = interpreted_functions_values

Expand Down Expand Up @@ -93,9 +97,7 @@ def supported_kind() -> ProblemKind:
supported_kind.set_fluents_type("OBJECT_FLUENTS")
supported_kind.set_conditions_kind("NEGATIVE_CONDITIONS")
supported_kind.set_conditions_kind("DISJUNCTIVE_CONDITIONS")
supported_kind.set_conditions_kind(
"INTERPRETED_FUNCTIONS_IN_CONDITIONS"
) # added this
supported_kind.set_conditions_kind("INTERPRETED_FUNCTIONS_IN_CONDITIONS")
supported_kind.set_conditions_kind("EQUALITIES")
supported_kind.set_conditions_kind("EXISTENTIAL_CONDITIONS")
supported_kind.set_conditions_kind("UNIVERSAL_CONDITIONS")
Expand All @@ -109,12 +111,8 @@ def supported_kind() -> ProblemKind:
supported_kind.set_effects_kind("FLUENTS_IN_NUMERIC_ASSIGNMENTS")
supported_kind.set_effects_kind("FLUENTS_IN_OBJECT_ASSIGNMENTS")
supported_kind.set_effects_kind("FORALL_EFFECTS")
supported_kind.set_effects_kind(
"INTERPRETED_FUNCTIONS_IN_BOOLEAN_ASSIGNMENTS"
) # added this
supported_kind.set_effects_kind(
"INTERPRETED_FUNCTIONS_IN_NUMERIC_ASSIGNMENTS"
) # added this
supported_kind.set_effects_kind("INTERPRETED_FUNCTIONS_IN_BOOLEAN_ASSIGNMENTS")
supported_kind.set_effects_kind("INTERPRETED_FUNCTIONS_IN_NUMERIC_ASSIGNMENTS")
supported_kind.set_time("CONTINUOUS_TIME")
supported_kind.set_time("DISCRETE_TIME")
supported_kind.set_time("INTERMEDIATE_CONDITIONS_AND_EFFECTS")
Expand All @@ -127,9 +125,7 @@ def supported_kind() -> ProblemKind:
supported_kind.set_expression_duration("FLUENTS_IN_DURATIONS")
supported_kind.set_expression_duration("INT_TYPE_DURATIONS")
supported_kind.set_expression_duration("REAL_TYPE_DURATIONS")
supported_kind.set_expression_duration(
"INTERPRETED_FUNCTIONS_IN_DURATIONS"
) # added this
supported_kind.set_expression_duration("INTERPRETED_FUNCTIONS_IN_DURATIONS")
supported_kind.set_simulated_entities("SIMULATED_EFFECTS")
supported_kind.set_constraints_kind("STATE_INVARIANTS")
supported_kind.set_quality_metrics("ACTIONS_COST")
Expand All @@ -142,8 +138,6 @@ def supported_kind() -> ProblemKind:
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")
return supported_kind

@staticmethod
Expand All @@ -165,6 +159,10 @@ def resulting_problem_kind(
if new_kind.has_interpreted_functions_in_durations():
new_kind.unset_expression_duration("INTERPRETED_FUNCTIONS_IN_DURATIONS")
new_kind.set_expression_duration("INT_TYPE_DURATIONS")
if new_kind.has_interpreted_functions_in_boolean_assignments():
new_kind.unset_effects_kind("INTERPRETED_FUNCTIONS_IN_BOOLEAN_ASSIGNMENTS")

Check warning on line 163 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L163

Added line #L163 was not covered by tests
if new_kind.has_interpreted_functions_in_numeric_assignments():
new_kind.unset_effects_kind("INTERPRETED_FUNCTIONS_IN_NUMERIC_ASSIGNMENTS")

Check warning on line 165 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L165

Added line #L165 was not covered by tests

return new_kind

Expand Down Expand Up @@ -234,28 +232,30 @@ def _compile(
# extract all effects
# if effects assigns IF value to fluent -> add fluent same as above
# fluent default at true (maybe it would be better to default it at false and flip the rest of the checks/assignments to reduce the number of Nots)
# e.g. the fluent could be called value_has_changed instead of known_value
# fluent keeps track of weather another fluent has been assigned a value from an IF
# add it to list of pairs (variable, fluent_tracking_variable)
# we will need this later when we create new actions

# NOTE right now implemented with the tracking fluent as thing_has_changed
# init to false, condition is "cond Or thing_has_changed", assignment in unknown case is to true
assignment_tracking_fluents: dict = {}
print("effects tests -------------------------------------------------------")
for a in problem.actions:
found_effects = self.get_effects(a)
for time, ef in found_effects:
print(time) # we don't need time here
print(ef.fluent)
print(ef.value)
ifuns = self.interpreted_functions_extractor.get(ef.value)
if len(ifuns) != 0:
# there are ifuns
# in the effect assigment
# of fluent ef.fluent
new_f_name = get_fresh_name(
new_problem, "_" + str(ef.fluent) + "_known"
)
f = Fluent(new_f_name, BoolType())
new_problem.add_fluent(f, default_initial_value=True)
print(f)
if ef.fluent not in assignment_tracking_fluents.keys():
ifuns = self.interpreted_functions_extractor.get(ef.value)
if len(ifuns) != 0:
print(type(ef.fluent)) # this is an FNode
print(type(ef.fluent.fluent())) # this is a fluent
new_f_name = get_fresh_name(

Check warning on line 252 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L250-L252

Added lines #L250 - L252 were not covered by tests
new_problem, "_" + str(ef.fluent.fluent()) + "_has_changed"
)
f = Fluent(new_f_name, BoolType())
new_problem.add_fluent(f, default_initial_value=False)
assignment_tracking_fluents[ef.fluent.fluent()] = f

Check warning on line 257 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L255-L257

Added lines #L255 - L257 were not covered by tests
print(assignment_tracking_fluents)
print(
"end of effects tests -------------------------------------------------------"
)
Expand All @@ -269,23 +269,40 @@ def _compile(
effs = []
if_conds = []
for t, c in self.get_conditions(a):
ifuns = self.interpreted_functions_extractor.get(c)
new_c = c
all_names: set = set()
# NOTE - probably should find better solution for this
# might have to change the names extractor
# maybe use free vars extractor
# TODO use fluent instead of fluentexpression
try:
all_names = self.names_extractor.extract_names(c)
except:
print("cannot extract some names")
print("if this has IF assignment in effects it might not work")

for k in assignment_tracking_fluents.keys():
if k.name in all_names:
print("found one, we have to change this condition")
new_c = em.Or(new_c, assignment_tracking_fluents[k])

Check warning on line 287 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L285-L287

Added lines #L285 - L287 were not covered by tests

ifuns = self.interpreted_functions_extractor.get(new_c)
if ifuns:
if_conds.append((t, c, ifuns, False, False, False))
if_conds.append((t, new_c, ifuns, False, False, False, None))
else:
conds.append((t, c))
conds.append((t, new_c))

# get all effects
for time, ef in self.get_effects(a):
ifuns = self.interpreted_functions_extractor.get(ef.value)
# check if they contain IFs
if len(ifuns) != 0:
# if they do save them to if_conds with is_effect
if_conds.append((time, ef.value, ifuns, False, False, True))

if_conds.append((time, ef.value, ifuns, False, False, True, ef))

Check warning on line 302 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L302

Added line #L302 was not covered by tests
else:
effs.append((time, ef))
# should we do a list of just ok effects similar to conds?
pass

# maybe we should change name for if_cond and use an enum for is_duration/is_lower and is_effect
# enum type {CONDITION, DURATION_LOWER, DURATION_UPPER, EFFECT};
Expand All @@ -295,12 +312,16 @@ def _compile(
lower = a.duration.lower
ifuns = self.interpreted_functions_extractor.get(lower)
if ifuns:
if_conds.append((StartTiming(), lower, ifuns, True, True, False))
if_conds.append(
(StartTiming(), lower, ifuns, True, True, False, None)
)
lower = None
upper = a.duration.upper
ifuns = self.interpreted_functions_extractor.get(upper)
if ifuns:
if_conds.append((StartTiming(), upper, ifuns, True, False, False))
if_conds.append(

Check warning on line 322 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L322

Added line #L322 was not covered by tests
(StartTiming(), upper, ifuns, True, False, False, None)
)
upper = None

Check warning on line 325 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L325

Added line #L325 was not covered by tests

for known in itertools.product([True, False], repeat=len(if_conds)):
Expand All @@ -313,9 +334,15 @@ def _compile(
new_conds = []
new_effs: list = []
paramcounter = 0
for (t, c, ifuns, is_duration, is_lower, is_effect), k in zip(
if_conds, known
):
for (
t,
c,
ifuns,
is_duration,
is_lower,
is_effect,
effect_instance,
), k in zip(if_conds, known):
subs = {}
implies = []
l1 = []
Expand Down Expand Up @@ -350,7 +377,8 @@ def _compile(
)
l2.append(pf)
else:
# in this case it means that this function has to be ocnsidered unknown
# NOTE - check if we need anything here
# in this case it means that this function has to be considered unknown
# and there are no known values to put as not in the condition
pass
if len(l2) != 0:
Expand All @@ -364,10 +392,13 @@ def _compile(
else:
upper = c.substitute(subs)

Check warning on line 393 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L393

Added line #L393 was not covered by tests
elif is_effect:
# case known effect
# TODO - effects stuff
# new_effect - substitute
print("we have an effect")
assert (

Check warning on line 395 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L395

Added line #L395 was not covered by tests
effect_instance is not None
) # NOTE - is doing this for fixing mypy problems ok?
n_e_v = c.substitute(subs)
n_e = effect_instance.clone()
n_e.set_value(n_e_v)
new_effs.append((t, n_e)) # NOTE - not tested

Check warning on line 401 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L398-L401

Added lines #L398 - L401 were not covered by tests
else:
new_conds.append((t, c.substitute(subs)))
else:
Expand All @@ -379,11 +410,22 @@ def _compile(
else:
upper = em.Real(Fraction(1000000, 1))

Check warning on line 411 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L411

Added line #L411 was not covered by tests
if is_effect:
pass
# case unknown effect
# TODO - new effect
# new effect - known_variable := false
assert (

Check warning on line 413 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L413

Added line #L413 was not covered by tests
effect_instance is not None
) # NOTE - is doing this for fixing mypy problems ok?
tracking_fluent_expression = em.FluentExp(

Check warning on line 416 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L416

Added line #L416 was not covered by tests
assignment_tracking_fluents[
effect_instance.fluent.fluent()
]
)
n_e = Effect(

Check warning on line 421 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L421

Added line #L421 was not covered by tests
tracking_fluent_expression, em.TRUE(), em.TRUE()
)
new_effs.append((t, n_e))

Check warning on line 424 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L424

Added line #L424 was not covered by tests

print("printing all effects")
print(effs)
print(new_effs)
new_a = self.clone_action_with_extra_params(
a, new_params, conds + new_conds, (lower, upper), effs + new_effs
)
Expand Down Expand Up @@ -422,6 +464,7 @@ def clone_action_with_extra_params(
# effects have to be managed slightly differently in instantaneous and durative
# for each fluent in the new arg, if condition contains it, add
# [condition] Or Not fluent_that_keeps_track
# this has to be done at the start

new_action: Optional[
up.model.DurativeAction | up.model.InstantaneousAction
Expand All @@ -436,7 +479,7 @@ def clone_action_with_extra_params(
for t, se in a.simulated_effects.items():
new_durative_action.set_simulated_effect(

Check warning on line 480 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L480

Added line #L480 was not covered by tests
t, se
) # TODO - check if this is correct
) # NOTE - this might not be correct
new_durative_action.clear_conditions()
for ii, c in conditions:
new_durative_action.add_condition(ii, c)
Expand Down Expand Up @@ -504,8 +547,6 @@ def get_effects(self, a):
for eff in d_aeffs[time]:
eff_list.append(eff)
time_list.append(time)
# TODO
pass
else:
# case default
raise NotImplementedError

Check warning on line 552 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L552

Added line #L552 was not covered by tests
Expand Down Expand Up @@ -584,7 +625,9 @@ def knowledge_compatible(_ic, _k, _kl):
retval = True
kifuns = []
ukifuns = []
for (t, c, ifuns, is_duration, is_lower, is_effect), k in zip(_ic, _k):
for (t, c, ifuns, is_duration, is_lower, is_effect, effect_instance), k in zip(
_ic, _k
):
if k:
for ifun in ifuns:
if ifun in ukifuns:
Expand All @@ -600,7 +643,7 @@ def knowledge_compatible(_ic, _k, _kl):
retval = False

Check warning on line 643 in unified_planning/engines/compilers/interpreted_functions_remover.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/compilers/interpreted_functions_remover.py#L643

Added line #L643 was not covered by tests
else:
ukifuns.append(ifun)
# TODO check for always impossible probably does not work with complex durative conditions
# NOTE check for always impossible probably does not work with complex durative conditions
# e.g. situation where you are supposed to know one same value at start but not at end

return retval
1 change: 1 addition & 0 deletions unified_planning/engines/interpreted_functions_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ def _attempt_to_solve(

def _refine(self, problem, validation_result):
newProb = None
# TODO - how to get new calculated IFs for assignments ?
if validation_result.calculated_interpreted_functions is None:
print("no updates available, the problem has no solution")

Check warning on line 265 in unified_planning/engines/interpreted_functions_planner.py

View check run for this annotation

Codecov / codecov/patch

unified_planning/engines/interpreted_functions_planner.py#L265

Added line #L265 was not covered by tests
elif len(validation_result.calculated_interpreted_functions) == 0:
Expand Down
8 changes: 1 addition & 7 deletions unified_planning/engines/plan_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,12 +633,6 @@ def _validate(
if not self._check_condition(state=state, se=se, condition=c):
if opt_ai is not None:
if hasif:
# cif = simulator._knowledge
# print(
# "this thing does not have a simulator like the sequential ones ;-;"
# )
# print ("now in validator, extracting knowledge")
# print(opt_ai.action)
ife: up.model.walkers.InterpretedFunctionsExtractor = (
up.model.walkers.InterpretedFunctionsExtractor()
)
Expand Down Expand Up @@ -673,7 +667,7 @@ def _validate(
)
cif[fp(*notOkParams)] = fc(
*notOkParams
) # does not use memoization
) # NOTE does not use memoization
return ValidationResult(
status=ValidationResultStatus.INVALID,
engine_name=self.name,
Expand Down
5 changes: 1 addition & 4 deletions unified_planning/engines/sequential_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ def get_unsatisfied_conditions(
ifs = ife.get(c)
# print (ifs)
for infu in ifs:
# TODO remove _things
r = evaluate(infu) # aka blockedvalue - in final output
fp = infu._content.payload # aka foundcon - in final output
fa = infu._content.args # aka args
Expand All @@ -481,10 +482,6 @@ def get_unsatisfied_conditions(
# print (state)
# print (infu._content.payload)

# print (self._contains_IFs)
# here evaluates the whole precondition - how do I fish up IF? -
# print (c._content) # ----------------------------------------------------
# print (evaluated_cond._content) # ---------------------------------------
if (
not evaluated_cond.is_bool_constant()
or not evaluated_cond.bool_constant_value()
Expand Down
2 changes: 1 addition & 1 deletion unified_planning/model/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ def FluentExp(
node_type=OperatorKind.FLUENT_EXP, args=tuple(params_exp), payload=fluent
)

def InterpretedFunctionExp( # not sure this is functional yet
def InterpretedFunctionExp(
self,
interpreted_function: "up.model.interpreted_function.InterpretedFunction",
params: Sequence[Expression] = tuple(),
Expand Down

0 comments on commit ef06156

Please sign in to comment.