aiplan4eu / up-lpg

LPG is a automated planner supporting classical and numeric state variables, and supports durative actions too. It computes plans using one-shoot and anytime operation mode.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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!