-1

I know that custom shadow variables are currently not supported in optapy, so is there a way to solve the optimization problem below: distribute tasks from the project among employees, given that the tasks have a clear order in which they must be performed and people have skills, depending on which the task execution time changes?

All ideas will be appreciated.

1 Answers1

1

Custom shadow variables ARE supported in optapy: https://www.optapy.org/docs/latest/shadow-variable/shadow-variable.html#customVariableListener ; It uses the old style of @CustomShadowVariable (@custom_shadow_variable in Python) instead of @ShadowVariable. However, ListVariableListener is not currently supported. You can use the old chained model of the domain and constraints, which should be fully supported in optapy. Support for the new @ShadowVariable and ListVariableListener will be added in a future version of optapy.

Here how the variable listener would look in Python:

from optapy import variable_listener

@variable_listener
class StartTimeUpdatingVariableListener:

    def beforeEntityAdded(self, scoreDirector, task):
        pass
    
    def afterEntityAdded(self, scoreDirector, task):
        self.updateStartTime(scoreDirector, task)
    
    def beforeVariableChanged(self, scoreDirector, task):
        pass

    def afterVariableChanged(self, scoreDirector, task):
        self.updateStartTime(scoreDirector, task)

    def beforeEntityRemoved(self, scoreDirector, task):
        pass

    def afterEntityRemoved(self, scoreDirector, task):
        pass

    def updateStartTime(self, scoreDirector, sourceTask):
        previous = sourceTask.getPreviousTaskOrEmployee()
        shadowTask = sourceTask
        previousEndTime = None if previous is None else previous.getEndTime()
        startTime = self.calculateStartTime(shadowTask, previousEndTime)
        while shadowTask is not None and shadowTask.getStartTime() != startTime:
            scoreDirector.beforeVariableChanged(shadowTask, "startTime")
            shadowTask.setStartTime(startTime)
            scoreDirector.afterVariableChanged(shadowTask, "startTime")
            previousEndTime = shadowTask.getEndTime()
            shadowTask = shadowTask.getNextTask()
            startTime = self.calculateStartTime(shadowTask, previousEndTime)

    def calculateStartTime(self, task, previousEndTime):
        if task is None or previousEndTime is None:
            return None
        return max(task.getReadyTime(), previousEndTime)

and here how it can be used in a @custom_shadow_variable:

from optapy import planning_entity, planning_variable, custom_shadow_variable, planning_variable_reference

@planning_entity
class Task:
    # ...
    @custom_shadow_variable(variable_listener_class = StartTimeUpdatingVariableListener,
            sources=[planning_variable_reference('previousTaskOrEmployee')])
    def get_start_time(self):
        return self.start_time
    
    def set_start_time(self, start_time):
        self.start_time = start_time
Christopher Chianelli
  • 1,163
  • 1
  • 8
  • 8