Source code for discrete_optimization.generic_tasks_tools.objectives.soft_time_penalty

#  Copyright (c) 2026 AIRBUS and its affiliates.
#  This source code is licensed under the MIT license found in the
#  LICENSE file in the root directory of this source tree.
from typing import Generic

from discrete_optimization.generic_tasks_tools.enums import MinOrMax, StartOrEnd
from discrete_optimization.generic_tasks_tools.generic_scheduling import (
    GenericSchedulingProblem,
    GenericSchedulingSolution,
    NonRenewableResource,
    NonSkillCumulativeResource,
    Skill,
    Task,
    UnaryResource,
)
from discrete_optimization.generic_tasks_tools.generic_scheduling_utils import Objective
from discrete_optimization.generic_tasks_tools.objectives.objective_computer import (
    ObjectiveComputer,
)


[docs] class SoftTimePenaltyComputer( ObjectiveComputer[Task], Generic[ Task, UnaryResource, Skill, NonSkillCumulativeResource, NonRenewableResource ], ): problem: GenericSchedulingProblem[ Task, UnaryResource, Skill, NonSkillCumulativeResource, NonRenewableResource ] penalty = 0
[docs] @staticmethod def get_objective_name() -> Objective: return Objective.TIME_PENALTY
[docs] def compute_objective( self, solution: GenericSchedulingSolution[ Task, UnaryResource, Skill, NonSkillCumulativeResource, NonRenewableResource ], ) -> float: penalty = 0 # time windows for task in self.problem.tasks_list: start = solution.get_start_time(task) end = solution.get_end_time(task) start_lb = self.problem.get_task_start_or_end_lower_bound( task=task, start_or_end=StartOrEnd.START ) end_lb = self.problem.get_task_start_or_end_lower_bound( task=task, start_or_end=StartOrEnd.END ) start_ub = self.problem.get_task_start_or_end_upper_bound( task=task, start_or_end=StartOrEnd.START ) end_ub = self.problem.get_task_start_or_end_upper_bound( task=task, start_or_end=StartOrEnd.END ) penalty += max(0, start_lb - start) penalty += max(0, end_lb - end) penalty += max(0, start - start_ub) penalty += max(0, end - end_ub) # time lags for task1_start_or_end in StartOrEnd: for task2_start_or_end in StartOrEnd: for min_or_max in MinOrMax: for task1, task2, offset in self.problem.get_original_time_lags( task1_start_or_end=task1_start_or_end, task2_start_or_end=task2_start_or_end, min_or_max=min_or_max, ): t1 = solution.get_start_or_end_time( task=task1, start_or_end=task1_start_or_end ) t2 = solution.get_start_or_end_time( task=task2, start_or_end=task2_start_or_end ) if min_or_max == MinOrMax.MIN: penalty += max(0, t1 + offset - t2) else: penalty += max(0, t2 - (t1 + offset)) return penalty