UP cannot parse plan with splitted actions.
jrenoux opened this issue · comments
Hi there,
I am having another issue with a
unified_planning.exceptions.UPException: Error parsing plan generated by LPGEngine
this time when the plan generates splitted actions.
The output from the planner is:
Plan computed:
0.0000: (action_annealing a4 o_17x gear) [D:3.0000; C:0.1000]
3.0000: (action_turning kr10 o_17x gear) [D:5.0000; C:0.1000]
3.0000: (action_annealing a4 o_17x pinion) [D:3.0000; C:0.1000]
6.0000: (action_turning kr15 o_17x pinion) [D:5.0000; C:0.1000]
11.0000: (action_lapping l o_17x) [D:10.0000; C:0.1000]
Here is the corresponding code. Sorry for the loooooooong file ^^
from collections import OrderedDict
import unified_planning as up
from unified_planning.shortcuts import *
"""A proof-of-concept example of the Meritor usecase
Contains a simplified and hard-coded version of the problem
Actual demonstrator should take in files from Meritor and process them to create the fluents and operators
"""
############################################
# Values that will get parsed from the meritor documents
#############################################
# TODO these are to be extracted from the meritor documents
final_output = 10
stock_annealed_gear = 0
stock_annealed_pinion = 0
stock_turned_gear = 0
stock_turned_pinion = 0
stock_lapped = 0
starting_stock_gear = 20
starting_stock_pinion = 20
############################################
# Calculate the amount to be processed for each step
#############################################
to_be_lapped = final_output - stock_lapped
to_be_turned_gear = to_be_lapped - stock_turned_gear
to_be_annealed_gear = to_be_turned_gear - stock_annealed_gear
print("To be annealed gear (existing stock): " + str(to_be_annealed_gear) + "(" + str(stock_annealed_gear) + ")")
print("To be turned gear (existing stock): " + str(to_be_turned_gear) + "(" + str(stock_turned_gear) + ")")
to_be_turned_pinion = max(to_be_lapped - stock_turned_pinion, 0)
to_be_annealed_pinion = max(to_be_turned_pinion - stock_annealed_pinion, 0)
print("To be annealed pinion (existing stock): " + str(to_be_annealed_pinion) + "(" + str(stock_annealed_pinion) + ")")
print("To be turned pinion (existing stock): " + str(to_be_turned_pinion) + "(" + str(stock_turned_pinion) + ")")
print("To be lapped (existing stock):" + str(to_be_lapped) + "(" + str(stock_lapped) + ")")
print("Final output: " + str(final_output))
############################################
# General UP things
#############################################
env = up.environment.get_env()
tm = env.type_manager
############################################
# Defining types
#############################################
type_family = tm.UserType("type_family")
type_machine = tm.UserType("type_machine")
type_piece = tm.UserType("type_piece")
type_step = tm.UserType("type_step")
############################################
# Defining objects
#############################################
# pieces
o_gear = Object("gear", type_piece)
o_pinion = Object("pinion", type_piece)
# families
o_family_17x = Object("17X", type_family)
# machines
o_machine_A4 = Object("A4", type_machine)
o_machine_KR10 = Object("KR10", type_machine)
o_machine_KR15 = Object("KR15", type_machine)
o_machine_L = Object("L", type_machine)
# process steps
o_step_annealing = Object("step_annealing", type_step)
o_step_turning = Object("step_turning", type_step)
o_step_lapping = Object("step_lapping", type_step)
############################################
# Initializing the problem
#############################################
mer_problem = Problem("meritor_problem")
mer_problem.add_object(o_gear)
mer_problem.add_object(o_family_17x)
mer_problem.add_object(o_machine_A4)
mer_problem.add_object(o_machine_KR10)
mer_problem.add_object(o_machine_KR15)
mer_problem.add_object(o_machine_L)
mer_problem.add_object(o_step_annealing)
mer_problem.add_object(o_step_turning)
mer_problem.add_object(o_step_lapping)
#############################################
# Required fluents for the propagation
#############################################
# fluent describing the EDI/Batch to be produced
# number of gears
f_buff_start = up.model.Fluent("buffer_start",
tm.IntType(),
family = type_family,
piece = type_piece)
mer_problem.add_fluent(f_buff_start)
mer_problem.set_initial_value(
f_buff_start(o_family_17x, o_gear),
starting_stock_gear
)
mer_problem.set_initial_value(
f_buff_start(o_family_17x, o_pinion),
starting_stock_pinion
)
# fluent that stored the expected amount of final output
f_final_output = up.model.Fluent("final_output",
tm.IntType(),
family = type_family)
mer_problem.add_fluent(f_final_output)
mer_problem.set_initial_value(f_final_output(o_family_17x), final_output)
#fluents describing existing stock after each process
f_stock_annealed = Fluent("stock_annealed",
IntType(),
family = type_family,
piece = type_piece)
mer_problem.add_fluent(f_stock_annealed)
mer_problem.set_initial_value(f_stock_annealed(o_family_17x, o_gear), stock_annealed_gear)
mer_problem.set_initial_value(f_stock_annealed(o_family_17x, o_pinion), stock_annealed_pinion)
f_stock_turned = Fluent("stock_turned",
IntType(),
family = type_family,
piece = type_piece)
mer_problem.add_fluent(f_stock_turned)
mer_problem.set_initial_value(f_stock_turned(o_family_17x, o_gear), stock_turned_gear)
mer_problem.set_initial_value(f_stock_turned(o_family_17x, o_pinion), stock_turned_pinion)
f_stock_lapped = Fluent("stock_lapped",
IntType(),
family=type_family)
mer_problem.add_fluent(f_stock_lapped)
mer_problem.set_initial_value(f_stock_lapped(o_family_17x), stock_lapped)
# fluent that backpropagate the amount of required pieces -> required for the next step - stock
f_to_be_turned = Fluent("to_be_turned",
IntType(),
family = type_family,
piece = type_piece)
mer_problem.add_fluent(f_to_be_turned)
mer_problem.set_initial_value(f_to_be_turned(o_family_17x, o_gear), to_be_turned_gear)
mer_problem.set_initial_value(f_to_be_turned(o_family_17x, o_pinion), to_be_turned_pinion)
f_to_be_annealed = Fluent("to_be_annealed",
IntType(),
family = type_family,
piece = type_piece)
mer_problem.add_fluent(f_to_be_annealed)
mer_problem.set_initial_value(f_to_be_annealed(o_family_17x, o_gear), to_be_annealed_gear)
mer_problem.set_initial_value(f_to_be_annealed(o_family_17x, o_pinion), to_be_annealed_pinion)
f_to_be_lapped = Fluent("to_be_lapped",
IntType(),
family=type_family)
mer_problem.add_fluent(f_to_be_lapped)
mer_problem.set_initial_value(f_to_be_lapped(o_family_17x), to_be_lapped)
#############################################
# Required fluents for the process
#############################################
# capacity of a machine for a given family and piece type
f_has_capacity = up.model.Fluent("has_capacity",
tm.IntType(),
machine = type_machine,
family = type_family,
piece = type_piece,
step = type_step
)
mer_problem.add_fluent(f_has_capacity)
mer_problem.set_initial_value(
f_has_capacity(o_machine_A4, o_family_17x, o_gear, o_step_annealing
),
1000
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_A4, o_family_17x, o_pinion, o_step_annealing
),
1000
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_A4, o_family_17x, o_gear, o_step_turning),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_A4, o_family_17x, o_pinion, o_step_turning),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_A4, o_family_17x, o_gear, o_step_lapping),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_A4, o_family_17x, o_pinion, o_step_lapping),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR10, o_family_17x, o_gear, o_step_annealing),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR10, o_family_17x, o_pinion, o_step_annealing),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR10, o_family_17x, o_gear, o_step_turning),
1000
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR10, o_family_17x, o_pinion, o_step_turning),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR10, o_family_17x, o_gear, o_step_lapping),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR10, o_family_17x, o_pinion, o_step_lapping),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR15, o_family_17x, o_gear, o_step_annealing),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR15, o_family_17x, o_pinion, o_step_annealing),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR15, o_family_17x, o_gear, o_step_turning),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR15, o_family_17x, o_pinion, o_step_turning),
1000
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR15, o_family_17x, o_gear, o_step_lapping),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_KR15, o_family_17x, o_pinion, o_step_lapping),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_L, o_family_17x, o_gear, o_step_annealing),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_L, o_family_17x, o_pinion, o_step_annealing),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_L, o_family_17x, o_gear, o_step_turning),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_L, o_family_17x, o_pinion, o_step_turning),
0
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_L, o_family_17x, o_gear, o_step_lapping),
1000
)
mer_problem.set_initial_value(
f_has_capacity(o_machine_L, o_family_17x, o_pinion, o_step_lapping),
1000
)
# number of elements currently processed in a given machine
f_nb_elements_in_machine = up.model.Fluent("nb_elements_in_machine",
tm.IntType(),
machine=type_machine)
mer_problem.add_fluent(f_nb_elements_in_machine)
mer_problem.set_initial_value(f_nb_elements_in_machine(o_machine_A4), 0)
mer_problem.set_initial_value(f_nb_elements_in_machine(o_machine_KR10), 0)
mer_problem.set_initial_value(f_nb_elements_in_machine(o_machine_KR15), 0)
mer_problem.set_initial_value(f_nb_elements_in_machine(o_machine_L), 0)
# fluent representing the duration of the annealing action per machine
f_annealing_duration = Fluent("annealing_duration", RealType(), machine = type_machine)
mer_problem.add_fluent(f_annealing_duration)
mer_problem.set_initial_value(f_annealing_duration(o_machine_A4), 3)
mer_problem.set_initial_value(f_annealing_duration(o_machine_KR10), 0)
mer_problem.set_initial_value(f_annealing_duration(o_machine_KR15), 0)
mer_problem.set_initial_value(f_annealing_duration(o_machine_L), 0)
f_turning_duration = Fluent("turning_duration", RealType(), machine=type_machine)
mer_problem.add_fluent(f_turning_duration)
mer_problem.set_initial_value(f_turning_duration(o_machine_A4), 0)
mer_problem.set_initial_value(f_turning_duration(o_machine_KR10), 5)
mer_problem.set_initial_value(f_turning_duration(o_machine_KR15), 5)
mer_problem.set_initial_value(f_turning_duration(o_machine_L), 0)
f_lapping_duration = Fluent("lapping_duration", RealType(), machine=type_machine)
mer_problem.add_fluent(f_lapping_duration)
mer_problem.set_initial_value(f_lapping_duration(o_machine_A4), 0)
mer_problem.set_initial_value(f_lapping_duration(o_machine_KR10), 0)
mer_problem.set_initial_value(f_lapping_duration(o_machine_KR15), 0)
mer_problem.set_initial_value(f_lapping_duration(o_machine_L), 10)
#############################################
# Operators / actions
#############################################
########################################################## Annealing
# we take a more general approach in which we anneal only what we need
a_annealing = DurativeAction(
"action_annealing",
machine=type_machine,
family=type_family,
piece=type_piece
)
annealing_machine = a_annealing.parameter("machine")
annealing_family = a_annealing.parameter("family")
annealing_piece = a_annealing.parameter("piece")
a_annealing.set_fixed_duration((f_annealing_duration(annealing_machine)))
a_annealing.add_condition(
StartTiming(),
And(
# this machine is not currently in used
Equals(f_nb_elements_in_machine(annealing_machine), 0),
# this machine can anneal
GT(f_has_capacity(annealing_machine, annealing_family, annealing_piece, o_step_annealing), 0),
# there is actually something to be done, i.e. we need to anneal some pieces and can't just take the stock
GT(f_to_be_annealed(annealing_family, annealing_piece), 0),
# there is enough items in the input buffer to perform the task
GE(f_buff_start(annealing_family, annealing_piece), f_to_be_annealed(annealing_family, annealing_piece))
)
)
# the machine is processing to_be_annealed items
# TODO maybe the capacity of the machine is not high enough! -> do capacity / quantity distinction here
a_annealing.add_effect(
StartTiming(),
f_nb_elements_in_machine(annealing_machine),
f_to_be_annealed(annealing_family, annealing_piece)
)
# the input buffer is reduced
a_annealing.add_effect(
StartTiming(),
f_buff_start(annealing_family, annealing_piece),
f_buff_start(annealing_family, annealing_piece) - f_to_be_annealed(annealing_family, annealing_piece)
)
# at the end, the machine is not in used anymore
a_annealing.add_effect(
EndTiming(),
f_nb_elements_in_machine(annealing_machine),
0
)
# at the end, the output buffer is increased
a_annealing.add_effect(
EndTiming(),
f_stock_annealed(annealing_family, annealing_piece),
f_stock_annealed(annealing_family, annealing_piece) + f_to_be_annealed(annealing_family, annealing_piece)
)
mer_problem.add_action(a_annealing)
########################################################## Turning
a_turning = DurativeAction(
"action_turning",
machine=type_machine,
family=type_family,
piece=type_piece
)
turning_machine = a_turning.parameter("machine")
turning_family = a_turning.parameter("family")
turning_piece = a_turning.parameter("piece")
a_turning.set_fixed_duration(f_turning_duration(turning_machine))
a_turning.add_condition(
StartTiming(),
And(
# this machine is not currently in used
Equals(f_nb_elements_in_machine(annealing_machine), 0),
# this machine can turn (used for step + capacity)
GT(f_has_capacity(turning_machine, turning_family, turning_piece, o_step_turning), 0),
# there is something to be done
GT(f_to_be_turned(turning_family, turning_piece), 0),
# there is enough items in the input buffer to perform the task
GE(f_stock_annealed(turning_family, turning_piece), f_to_be_turned(turning_family, turning_piece)),
)
)
# the machine is processing
a_turning.add_effect(
StartTiming(),
f_nb_elements_in_machine(turning_machine),
f_to_be_turned(turning_family, turning_piece)
)
# the input buffer is reduced
a_turning.add_effect(
StartTiming(),
f_stock_annealed(turning_family, turning_piece),
f_stock_annealed(turning_family, turning_piece) - f_to_be_turned(turning_family, turning_piece)
)
# at the end the machine is not used anymore
a_turning.add_effect(
EndTiming(),
f_nb_elements_in_machine(turning_machine),
0
)
# at the end the output buffer is increased:
a_turning.add_effect(
EndTiming(),
f_stock_turned(turning_family,turning_piece),
f_stock_turned(turning_family, turning_piece) + f_to_be_turned(turning_family, turning_piece)
)
mer_problem.add_action(a_turning)
########################################################## Lapping
# TODO
a_lapping = DurativeAction("action_lapping",
machine=type_machine,
family=type_family
)
lapping_machine = a_lapping.parameter("machine")
lapping_family = a_lapping.parameter("family")
a_lapping.set_fixed_duration(f_lapping_duration(lapping_machine))
a_lapping.add_condition(
StartTiming(),
# the machine is not currently in use
And(
Equals(f_nb_elements_in_machine(lapping_machine), 0),
# the machine can lap
GT(f_has_capacity(lapping_machine, lapping_family, o_gear, o_step_lapping), 0),
GT(f_has_capacity(lapping_machine, lapping_family, o_pinion, o_step_lapping), 0),
# there is actually something to lap
GT(f_to_be_lapped(lapping_family), 0),
# there is enough items in both input buffers
GE(f_stock_turned(lapping_family, o_gear), f_to_be_lapped(lapping_family)),
GE(f_stock_turned(lapping_family, o_pinion), f_to_be_lapped(lapping_family))
)
)
# the machine is processing
a_lapping.add_effect(
StartTiming(),
f_nb_elements_in_machine(lapping_machine),
f_to_be_lapped(lapping_family)
)
# we reduce the size of the input buffers
a_lapping.add_effect(
StartTiming(),
f_stock_turned(lapping_family, o_gear),
f_stock_turned(lapping_family, o_gear) - f_to_be_lapped(annealing_family)
)
a_lapping.add_effect(
StartTiming(),
f_stock_turned(lapping_family, o_pinion),
f_stock_turned(lapping_family, o_pinion) - f_to_be_lapped(annealing_family)
)
# at the end the machine is freed
a_lapping.add_effect(
EndTiming(),
f_nb_elements_in_machine(lapping_machine),
0
)
# at the end, the output buffer is increased
a_lapping.add_effect(
EndTiming(),
f_stock_lapped(lapping_family),
f_stock_lapped(lapping_family) + f_to_be_lapped(lapping_family)
)
mer_problem.add_action(a_lapping)
########################################################## Goal
# the goal is that the output buffer equals the number of required items
mer_problem.add_goal(Equals(f_stock_lapped(o_family_17x), f_final_output(o_family_17x)))
env = mer_problem.env
factory = env.factory
factory.add_engine(name = "lpg", module_name = "up_lpg.lpg_planner", class_name = "LPGEngine")
with OneshotPlanner(name="lpg") as planner:
result = planner.solve(mer_problem, output_stream = sys.stdout)
if result.status == up.engines.PlanGenerationResultStatus.SOLVED_SATISFICING:
print("LPG returned: %s" % result.plan)
else:
print("No plan found.")
Hi @jrenoux i change the parser.
Can you try the last version?
thanks you
Still get the same issue with 0.4.7.
Traceback (most recent call last):
File "/.../up-mer-usecase/tests_up/pddl_parse_lgp_solve.py", line 532, in <module>
result = planner.solve(mer_problem, output_stream = sys.stdout)
File "/.../.cache/pypoetry/virtualenvs/aiplan4eu-mer-F4EqWIfX-py3.10/lib/python3.10/site-packages/unified_planning/engines/mixins/oneshot_planner.py", line 81, in solve
return self._solve(problem, heuristic, timeout, output_stream)
File "/.../.cache/pypoetry/virtualenvs/aiplan4eu-mer-F4EqWIfX-py3.10/lib/python3.10/site-packages/unified_planning/engines/pddl_planner.py", line 255, in _solve
plan = self._plan_from_file(
File "/.../.cache/pypoetry/virtualenvs/aiplan4eu-mer-F4EqWIfX-py3.10/lib/python3.10/site-packages/up_lpg/lpg_planner.py", line 63, in _plan_from_file
raise UPException('Error parsing plan generated by ' + self.__class__.__name__)
unified_planning.exceptions.UPException: Error parsing plan generated by LPGEngine
Hi @jrenoux, you install every time new version from pip?
I deploy a new version 0.0.4.8 one hour ago
I updated this morning, but probably more or less at the same time as you deployed.
With 0.0.4.8 it works fine now! Thanks!