timefold-solver icon indicating copy to clipboard operation
timefold-solver copied to clipboard

Code stopped working after Python upgrade

Open JelleVE opened this issue 1 year ago • 1 comments

Describe the bug After upgrading Python from version 3.10.12 to 3.11.10 my previously working code stopped working.

To Reproduce I believe this is the relevant bit of code:

@planning_entity
class TF_Assignment(JsonDomainBase):
    id: Annotated[int, PlanningId]
    language: Optional[str]
    location: Annotated[TF_Location, LocationSerializer, LocationValidator]
    duration_calculator: Annotated[TF_DurationCalculator, None]
    product: str
    min_start_time: datetime
    max_start_time: Annotated[datetime | None, None] # only used in case of fixed task
    max_end_time: datetime
    day_part: str
    is_fixed: bool
    fixed_expert: Optional['TF_Expert'] = None
    estimated_driving_time: Annotated[timedelta, DurationSerializer]
    real_driving_time: Annotated[timedelta, DurationSerializer]
    execution_time: Annotated[timedelta, DurationSerializer]
    expert: Annotated[Optional['TF_Expert'],
                       InverseRelationShadowVariable(source_variable_name='assignments'),
                       Field(default=None)]
    previous_assignment: Annotated[Optional['TF_Assignment'],
                              PreviousElementShadowVariable(source_variable_name='assignments'),
                              Field(default=None)]
    next_assignment: Annotated[Optional['TF_Assignment'],
                              NextElementShadowVariable(source_variable_name='assignments'),
                              Field(default=None)]
    arrival_time: Annotated[
        Optional[datetime],
        CascadingUpdateShadowVariable(target_method_name='update_arrival_time'),
        Field(default=None)]

    @computed_field
    @property
    def departure_time(self) -> Optional[datetime]:
        return self.calculate_departure_time()

    def update_arrival_time(self):
        if self.expert is None or (self.previous_assignment is not None and self.previous_assignment.arrival_time is None):
            self.arrival_time = None
        elif self.previous_assignment is None:
            if self.product.startswith('VEN_T5'):
                self.real_driving_time = timedelta(minutes=0.0)
                self.arrival_time = max(self.expert.min_first_arrival - self.real_driving_time, self.expert.min_start_time) + self.real_driving_time
            else:
                ref_prev = self.expert.name
                ref_current = self.id
                self.real_driving_time = timedelta(minutes=self.duration_calculator.calculateDuration(ref_prev, ref_current))
                self.arrival_time = max(self.expert.min_first_arrival - self.real_driving_time, self.expert.min_start_time) + self.real_driving_time
        else:
            #remainder of method

    def calculate_departure_time(self):
        if self.arrival_time is None:
            return None
        return max(self.arrival_time, self.min_start_time) + (self.execution_time * self.expert.speed)

This is the line that triggers the exception:

self.arrival_time = max(self.expert.min_first_arrival - self.real_driving_time, self.expert.min_start_time) + self.real_driving_time
Traceback (most recent call last):
  File "DefaultSolver.java", line 197, in ai.timefold.solver.core.impl.solver.DefaultSolver.solve
ai.timefold.jpyinterpreter.types.errors.ai.timefold.jpyinterpreter.types.errors.AttributeError: ai.timefold.jpyinterpreter.types.errors.AttributeError: object '<class datetime>' does not have attribute '__iter__'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "DefaultSolver.java", line 197, in ai.timefold.solver.core.impl.solver.DefaultSolver.solve
Exception: Java Exception

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/.venv/lib/python3.11/site-packages/timefold/solver/_solver.py", line 109, in solve
    java_solution = self._delegate.solve(java_problem)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
java.lang.java.lang.IllegalStateException: java.lang.IllegalStateException: The property ($method$update_arrival_time) getterMethod (public ai.timefold.jpyinterpreter.PythonLikeObject org.jpyinterpreter.user.tf_domain.TF_Assignment.$method$update_arrival_time()) on bean of class (class org.jpyinterpreter.user.tf_domain.TF_Assignment) throws an exception.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/main.py", line 82, in <module>
    main()
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/main.py", line 77, in main
    planner.simulate()
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/planner.py", line 76, in simulate
    self.simulateDay()
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/planner.py", line 162, in simulateDay
    solver.solve(self.logger)
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/tf_solver.py", line 157, in solve
    self.solution = solver.solve(self.solution)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jelle/Documents/Projects/work/code/planning-simulator/auto/.venv/lib/python3.11/site-packages/timefold/solver/_solver.py", line 113, in solve
    raise RuntimeError(f'Solving failed due to an error: {e.getMessage()}.\n'
RuntimeError: Solving failed due to an error: The property ($method$update_arrival_time) getterMethod (public ai.timefold.jpyinterpreter.PythonLikeObject org.jpyinterpreter.user.tf_domain.TF_Assignment.$method$update_arrival_time()) on bean of class (class org.jpyinterpreter.user.tf_domain.TF_Assignment) throws an exception..
Java stack trace: java.lang.IllegalStateException: The property ($method$update_arrival_time) getterMethod (public ai.timefold.jpyinterpreter.PythonLikeObject org.jpyinterpreter.user.tf_domain.TF_Assignment.$method$update_arrival_time()) on bean of class (class org.jpyinterpreter.user.tf_domain.TF_Assignment) throws an exception.
	at ai.timefold.solver.core.impl.domain.common.accessor.ReflectionMethodMemberAccessor.executeGetter(ReflectionMethodMemberAccessor.java:76)
	at ai.timefold.solver.core.impl.domain.variable.cascade.CascadingUpdateShadowVariableDescriptor.updateSingle(CascadingUpdateShadowVariableDescriptor.java:66)
	at ai.timefold.solver.core.impl.domain.variable.cascade.CascadingUpdateShadowVariableDescriptor.update(CascadingUpdateShadowVariableDescriptor.java:57)
	at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.evaluateFromIndex(VariableListenerSupport.java:286)
	at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerCascadingUpdateShadowVariableUpdate(VariableListenerSupport.java:275)
	at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:258)
	at ai.timefold.solver.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:322)
	at ai.timefold.solver.core.impl.move.director.VariableChangeRecordingScoreDirector.triggerVariableListeners(VariableChangeRecordingScoreDirector.java:162)
	at ai.timefold.solver.core.impl.heuristic.move.AbstractMove.doMoveOnly(AbstractMove.java:27)
	at ai.timefold.solver.core.impl.heuristic.move.LegacyMoveAdapter.execute(LegacyMoveAdapter.java:54)
	at ai.timefold.solver.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:265)
	at ai.timefold.solver.core.impl.constructionheuristic.decider.ConstructionHeuristicDecider.doMove(ConstructionHeuristicDecider.java:158)
	at ai.timefold.solver.core.impl.constructionheuristic.decider.ConstructionHeuristicDecider.decideNextStep(ConstructionHeuristicDecider.java:113)
	at ai.timefold.solver.core.impl.constructionheuristic.DefaultConstructionHeuristicPhase.solve(DefaultConstructionHeuristicPhase.java:63)
	at ai.timefold.solver.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:83)
	at ai.timefold.solver.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:197)
	Suppressed: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
		at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
		at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
		at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
		at java.base/java.util.Objects.checkIndex(Objects.java:385)
		at java.base/java.util.ArrayList.get(ArrayList.java:427)
		at ai.timefold.jpyinterpreter.types.collections.PythonLikeList.get(PythonLikeList.java:518)
		at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.evaluateFromIndex(VariableListenerSupport.java:286)
		at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerCascadingUpdateShadowVariableUpdate(VariableListenerSupport.java:275)
		at ai.timefold.solver.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:258)
		at ai.timefold.solver.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:322)
		at ai.timefold.solver.core.impl.move.director.ListVariableBeforeChangeAction.undo(ListVariableBeforeChangeAction.java:19)
		at ai.timefold.solver.core.impl.move.director.VariableChangeRecordingScoreDirector.undoChanges(VariableChangeRecordingScoreDirector.java:64)
		at ai.timefold.solver.core.impl.move.director.EphemeralMoveDirector.close(EphemeralMoveDirector.java:47)
		at ai.timefold.solver.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:264)
		... 5 more
Caused by: ai.timefold.jpyinterpreter.types.errors.AttributeError: object '<class datetime>' does not have attribute '__iter__'
	at ai.timefold.jpyinterpreter.PythonLikeObject.$getAttributeOrError(PythonLikeObject.java:42)
	at ai.timefold.jpyinterpreter.builtins.GlobalBuiltins.max(GlobalBuiltins.java:1202)
	at org.jpyinterpreter.user.tf_domain.TF_Assignment.update_arrival_time.invoke(/home/jelle/Documents/Projects/work/code/planning-simulator/auto/tf_domain.py:105)
	at org.jpyinterpreter.user.tf_domain.TF_Assignment.$method$update_arrival_time(/home/jelle/Documents/Projects/work/code/planning-simulator/auto/tf_domain.py:94)
	at ai.timefold.solver.core.impl.domain.common.accessor.ReflectionMethodMemberAccessor.executeGetter(ReflectionMethodMemberAccessor.java:73)
	... 15 more

Environment

Timefold Solver Version or Git ref: 1.17.0b0

Output of java -version: openjdk version "17.0.13" 2024-10-15 OpenJDK Runtime Environment (build 17.0.13+11-Ubuntu-2ubuntu122.04) OpenJDK 64-Bit Server VM (build 17.0.13+11-Ubuntu-2ubuntu122.04, mixed mode, sharing)

Output of uname -a or ver: Linux jelle-XPS-15-9520 6.8.0-49-generic #49~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Nov 6 17:42:15 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

JelleVE avatar Dec 13 '24 16:12 JelleVE

Thanks for reporting, @JelleVE! We'll take a look when time permits.

triceo avatar Dec 13 '24 16:12 triceo

As of today, Timefold halted any further development of the Python solver. Find out more here: https://github.com/TimefoldAI/timefold-solver/discussions/1698

Closing this issue.

triceo avatar Jul 11 '25 11:07 triceo