英文:
Optapy domain definitions help. Getting error: java.lang.VerifyError: Bad type on operand stack
问题
我正在尝试编写一个飞行员排班项目,其中需要分配飞行员和副驾驶员到航班("Duty")。我使用Python是因为客户已经在Python方面有一些投资。
我正在基于optapy员工排班快速入门来开展这个POC。
问题出现在我的domain.py
文件中,尝试初始化我的Availability
类时。具体地说,在包装它的@optapy.problem_fact
装饰器中出现了问题。optapy\annotations.py, 第585行
。
错误如下:
异常已发生: java.lang.VerifyError(注意: 显示了完整的异常跟踪,但执行已暂停在: _run_module_as_main)
java.lang.VerifyError: 操作数栈上的错误类型
异常详情:
位置:
org/jpyinterpreter/user/enum/Enum/__new__$$2.invoke(Lorg/jpyinterpreter/user/domain/AvailabilityType;Lorg/optaplanner/jpyinterpreter/PythonLikeObject;)Lorg/optaplanner/jpyinterpreter/PythonLikeObject; @897: invokestatic
原因:
类型 'org/optaplanner/jpyinterpreter/PythonLikeObject'(当前帧,堆栈[1])无法分配给 'org/optaplanner/jpyinterpreter/types/PythonLikeType'
当前帧:
bci: @897
标志: { }
本地变量: { 'org/jpyinterpreter/user/enum/Enum/__new__$$2', 'org/jpyinterpreter/user/domain/AvailabilityType', 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/jpyinterpreter/user/domain/AvailabilityType', 'org/optaplanner/jpyinterpreter/PythonLikeObject', top, 'org/optaplanner/jpyinterpreter/types/PythonNone', top, top, top, null, 'org/optaplanner/jpyinterpreter/types/collections/PythonLikeTuple', top, top, top, top, top, 'java/lang/Object', 'org/optaplanner/jpyinterpreter/PythonLikeObject' }
堆栈: { 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/optaplanner/jpyinterpreter/PythonLikeObject' }
字节码:
0000000: b200 5a3a 0b2b 4e2c 3a04 2ac0 0002 b400
... ... ...
如果您需要关于特定问题的帮助,请提供更多详细信息,以便我能更好地理解并提供更具体的建议。
英文:
I am trying to write a pilot rostering project, where pilots and copilots need to be assigned to flights ("Duty"). I'm using python because there are existing python investments at the client.
I'm basing this POC off of the optapy Employee Scheduling quickstart.
The code is crashing in my domain.py
, while trying to init my Availability class.
Specifically, in the @optapy.problem_fact
decorator wrapping it. optapy\annotations.py, line 585
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\PythonClassTranslator.java", line 292, in org.optaplanner.jpyinterpreter.PythonClassTranslator.translatePythonClass
Exception: Java Exception
The above exception was the direct cause of the following exception:
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 1220, in translate_python_class_to_java_class
out = PythonClassTranslator.translatePythonClass(python_compiled_class)
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 422, in convert_to_java_python_like_object
out = translate_python_class_to_java_class(raw_type)
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 399, in convert_to_java_python_like_object
convert_to_java_python_like_object(map_value, instance_map))
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\jpyinterpreter\python_to_java_bytecode_translator.py", line 1193, in translate_python_class_to_java_class
static_attributes_map.put(attribute[0], convert_to_java_python_like_object(attribute[1]))
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\optapy\optaplanner_java_interop.py", line 1050, in compile_and_get_class
parent_class = translate_python_class_to_java_class(python_class).getJavaClass()
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\optapy\optaplanner_java_interop.py", line 1063, in _generate_problem_fact_class
parent_class = compile_and_get_class(python_class)
File "C:\Users\andre\AppData\Local\Programs\Python\Python39\Lib\site-packages\optapy\annotations.py", line 585, in problem_fact
out.__optapy_java_class = _generate_problem_fact_class(fact_class)
File "C:\Users\andre\Desktop\Pilot Rosterer 3000\domain.py", line 131, in <module>
class Availability:
The error is as following:
Exception has occurred: java.lang.VerifyError (note: full exception trace is shown but execution is paused at: _run_module_as_main)
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
org/jpyinterpreter/user/enum/Enum/__new__$$2.invoke(Lorg/jpyinterpreter/user/domain/AvailabilityType;Lorg/optaplanner/jpyinterpreter/PythonLikeObject;)Lorg/optaplanner/jpyinterpreter/PythonLikeObject; @897: invokestatic
Reason:
Type 'org/optaplanner/jpyinterpreter/PythonLikeObject' (current frame, stack[1]) is not assignable to 'org/optaplanner/jpyinterpreter/types/PythonLikeType'
Current Frame:
bci: @897
flags: { }
locals: { 'org/jpyinterpreter/user/enum/Enum/__new__$$2', 'org/jpyinterpreter/user/domain/AvailabilityType', 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/jpyinterpreter/user/domain/AvailabilityType', 'org/optaplanner/jpyinterpreter/PythonLikeObject', top, 'org/optaplanner/jpyinterpreter/types/PythonNone', top, top, top, null, 'org/optaplanner/jpyinterpreter/types/collections/PythonLikeTuple', top, top, top, top, top, 'java/lang/Object', 'org/optaplanner/jpyinterpreter/PythonLikeObject' }
stack: { 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/optaplanner/jpyinterpreter/PythonLikeObject', 'org/optaplanner/jpyinterpreter/PythonLikeObject' }
Bytecode:
0000000: b200 5a3a 0b2b 4e2c 3a04 2ac0 0002 b400
... ... ...
domain.py
import optapy
import optapy.types
import optapy.score
import datetime
import enum
class Route:
name: str
duration: int
base: str
def __init__(self, name: str = None, duration: int = None, base: str = None):
self.name = name
self.duration = duration
self.base = base
class AvailabilityType(enum.Enum):
DESIRED = 'DESIRED'
UNDESIRED = 'UNDESIRED'
UNAVAILABLE = 'UNAVAILABLE'
@staticmethod
def list():
return list(map(lambda at: at, AvailabilityType))
@optapy.problem_fact
class Pilot:
name: str
skill_set: list[str]
home_base: str
duties: list # list[Duty]
def __init__(self, name: str = None, skill_set: list[str] = None, home_base: str = None, duties: list = None):
self.name = name
self.skill_set = skill_set
self.home_base = home_base
if duties is None:
self.duties = []
else:
self.duties = duties
def get_total_hours(self): # flight hours
dt = datetime.timedelta()
for duty in self.duties:
if duty.work_type != "Flying": continue # skip non-flight duties
dt += duty.end - duty.start
return dt.seconds/3600 + dt.days*24
def get_todays_hours(self, date: datetime.date):
dt = datetime.timedelta()
for duty in self.duties:
if duty.work_type != "Flying": continue # skip non-flight duties
if duty.start.date() != date: continue # skip duties from other days
dt += duty.end - duty.start # not taking into account flights over midnight. TODO: fix
return dt.seconds/3600 + dt.days*24
def get_total_overtime_hours(self):
days = dict()
for duty in self.duties:
if duty.work_type != "Flying": continue # skip non-flight duties
day = duty.start.date()
days[day] = days.get(day, datetime.timedelta()) + duty.end - duty.start
total_overtime = datetime.timedelta()
MAX_FLIGHT_HOURS_ALLOWED = datetime.timedelta(hours=10)
for day in days:
if day > MAX_FLIGHT_HOURS_ALLOWED:
total_overtime += day - MAX_FLIGHT_HOURS_ALLOWED
return total_overtime.seconds/3600 + total_overtime.days*24
def __str__(self):
return f'Pilot(name={self.name})'
def to_dict(self):
return {
'name': self.name,
'skill_set': self.skill_set,
'home_base': self.home_base,
'duties': self.duties
}
def duty_pinning_filter(solution, duty):
return not solution.schedule_state.is_draft(duty)
@optapy.planning_entity(pinning_filter=duty_pinning_filter)
@optapy.planning_pin
class Duty:
id: int
start: datetime.datetime
end: datetime.datetime
work_type: str
detail: str
required_skill: str
pilot: Pilot
# coPilot: Pilot # REMOVED: No longer complicating duties with 2 pilots, rather just creating seperate pilot duties and copilot duties for each route. This allows us to model duties other than flights
bases: list[str] # locaiotn(s) from which the duty can be performed
def __init__(self, id: int = None, start: datetime.datetime = None, end: datetime.datetime = None, work_type: str = None, detail: str = None, required_skill: str = None, pilot: Pilot = None, bases: list[str] = None):
self.id = id
self.start = start
self.end = end
self.work_type = work_type
self.detail = detail
self.required_skill = required_skill
self.pilot = pilot
self.bases = bases
@optapy.planning_id
def get_id(self):
return self.id
def __str__(self):
return f'Duty:{self.id}\t | {self.work_type}, detail={self.detail}, pilot={self.pilot}, start={self.start}, end={self.end}, required_skill={self.required_skill}, bases={self.bases})'
def to_dict(self):
return {
'id': self.id,
'work_type': self.work_type,
'start': self.start.isoformat(),
'end': self.end.isoformat(),
'detail': self.detail,
'required_skill': self.required_skill,
'pilot': self.pilot.to_dict() if self.pilot is not None else None,
'bases': self.bases
}
# ============================================================================================
# This is to get around the circular reference problem.
# The optapy decorators cannot refence Duty/Pilot before the classes are instantiated,
# but the classes rely on eachother being instantiated first before they can instatiated
# themselves. Hence the circular reference issue.
@optapy.planning_list_variable(Duty, ['duties_list'])
def get_duties(self):
return self.duties
def set_duties(self, duties):
self.duties = duties
@optapy.planning_variable(Pilot, value_range_provider_refs=['pilot_range'])
def get_pilot(self):
return self.pilot
def set_pilot(self, pilot):
self.pilot = pilot
Pilot.get_duties = get_duties
Pilot.set_duties = set_duties
Duty.get_pilot = get_pilot
Duty.set_pilot = set_pilot
# ============================================================================================
@optapy.problem_fact
class Availability:
pilot: Pilot
date: datetime.date
availability_type: AvailabilityType
def __init__(self, pilot: Pilot = None, date: datetime.date = None, availability_type: AvailabilityType = None):
self.pilot = pilot
self.date = date
self.availability_type = availability_type
def __str__(self):
return f'Availability(pilot={self.pilot}, date={self.date}, availability_type={self.availability_type})'
def to_dict(self):
return {
'pilot': self.pilot.to_dict(),
'date': self.date.isoformat(),
'availability_type': self.availability_type.value
}
class ScheduleState:
publish_length: int
draft_length: int
first_draft_date: datetime.date
last_historic_date: datetime.date
def __init__(self, publish_length: int = None, draft_length: int = None, first_draft_date: datetime.date = None, last_historic_date: datetime.date = None):
self.publish_length = publish_length
self.draft_length = draft_length
self.first_draft_date = first_draft_date
self.last_historic_date = last_historic_date
def is_draft(self, duty):
return duty.start >= datetime.datetime.combine(self.first_draft_date, datetime.time.min)
def to_dict(self):
return {
'publish_length': self.publish_length,
'draft_length': self.draft_length,
'first_draft_date': self.first_draft_date.isoformat(),
'last_historic_date': self.last_historic_date.isoformat()
}
@optapy.planning_solution
class PilotSchedule:
schedule_state: ScheduleState
availability_list: list[Availability]
pilot_list: list[Pilot]
duty_list: list[Duty]
solver_status: optapy.types.SolverStatus
score: optapy.score.SimpleScore
def __init__(self, schedule_state, availability_list, pilot_list, duty_list, solver_status, score=None):
self.pilot_list = pilot_list
self.availability_list = availability_list
self.schedule_state = schedule_state
self.duty_list = duty_list
self.solver_status = solver_status
self.score = score
@optapy.problem_fact_collection_property(Pilot)
@optapy.value_range_provider('pilot_range')
def get_pilot_list(self):
return self.pilot_list
@optapy.problem_fact_collection_property(Availability)
def get_availability_list(self):
return self.availability_list
@optapy.planning_entity_collection_property(Duty)
def get_duty_list(self):
return self.duty_list
@optapy.planning_score(optapy.score.HardSoftScore)
def get_score(self):
return self.score
def set_score(self, score):
self.score = score
def to_dict(self):
return {
'pilot_list': list(map(lambda pilot: pilot.to_dict(), self.pilot_list)),
'availability_list': list(map(lambda availability: availability.to_dict(), self.availability_list)),
'schedule_state': self.schedule_state.to_dict(),
'duty_list': list(map(lambda duty: duty.to_dict(), self.duty_list)),
'solver_status': self.solver_status.toString(),
'score': self.score.toString(),
}
答案1
得分: 1
这是OptaPy的一个错误(请参阅https://github.com/optapy/optapy/issues/146)。特别是,当翻译一个enum.Enum
类时,似乎是因为Availability
对AvailabilityType
进行了类型注解而引起的(AvailabilityType
是一个枚举)。移除类型注解可能会修复这个问题(因为那样AvailabilityType
不会被翻译,直到解决,这更宽容一些(因为翻译会导致错误,它将被翻译为指向CPython
类的指针)。当问题得到修复时,将更新此答案。
英文:
This is a bug with OptaPy (see https://github.com/optapy/optapy/issues/146). In particular, it seems to be caused when translating an enum.Enum
class (which is caused because Availability has type annotation for AvailabilityType, an enum). Removing the type annotation might fix the issue (since then AvailabilityType
will not be translated until solving, which is more forgiving (since the translation results in an error, it will be translated as a pointer to a CPython
class)). Will update this answer when the issue is fixed.
答案2
得分: 0
从Python 3.9.0升级到3.11.1解决了我的问题。
英文:
Updating from Python 3.9.0 to 3.11.1 solved the issue for me.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论