# Copyright (c) 2025 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 List, Optional
import numpy as np
from discrete_optimization.generic_tools.do_problem import (
EncodingRegister,
ModeOptim,
ObjectiveDoc,
ObjectiveHandling,
ObjectiveRegister,
ParamsObjectiveFunction,
Problem,
Solution,
TypeAttribute,
TypeObjective,
)
[docs]
class WTSolution(Solution):
def __init__(
self, problem: "WeightedTardinessProblem", schedule: list[tuple[int, int]]
):
self.problem = problem
self.schedule = schedule
[docs]
class WeightedTardinessProblem(Problem):
"""
Represents a single instance of the single-machine weighted tardiness problem.
"""
def __init__(
self,
num_jobs: int,
processing_times: List[int],
weights: List[int],
due_dates: List[int],
release_dates: Optional[List[int]] = None,
):
if not (len(processing_times) == len(weights) == len(due_dates) == num_jobs):
raise ValueError(f"All lists must contain {num_jobs} elements.")
self.num_jobs = num_jobs
self.processing_times = processing_times
self.weights = weights
self.due_dates = due_dates
self.release_dates = release_dates
if self.release_dates is None:
self.has_release = False
self.release_dates = [0 for i in range(self.num_jobs)]
# We still put some dummy values in case we use models considering release...
else:
self.has_release = True
def __repr__(self):
return (
f"WeightedTardinessProblem(num_jobs={self.num_jobs}, "
f"processing_times=..., weights=..., due_dates=...)"
)
[docs]
def evaluate(self, variable: WTSolution) -> dict[str, float]:
return {
"tardiness": sum(
[
self.weights[i]
* max(variable.schedule[i][-1] - self.due_dates[i], 0)
for i in range(len(self.due_dates))
]
)
}
[docs]
def satisfy(self, variable: WTSolution) -> bool:
max_t = max([x[1] for x in variable.schedule])
active_machine = np.zeros((max_t + 1))
for i in range(self.num_jobs):
active_machine[variable.schedule[i][0] : variable.schedule[i][1]] += 1
if np.max(active_machine) > 1:
return False
return True
[docs]
def get_attribute_register(self) -> EncodingRegister:
return EncodingRegister(
dict_attribute_to_type={"schedule": {"n": self.num_jobs}}
)
[docs]
def get_solution_type(self) -> type[Solution]:
return WTSolution
[docs]
def get_objective_register(self) -> ObjectiveRegister:
return ObjectiveRegister(
objective_sense=ModeOptim.MINIMIZATION,
objective_handling=ObjectiveHandling.SINGLE,
dict_objective_to_doc={
"tardiness": ObjectiveDoc(
type=TypeObjective.OBJECTIVE, default_weight=1
)
},
)
[docs]
def get_dummy_solution(self) -> WTSolution:
schedule = []
time_ = 0
for i in range(self.num_jobs):
schedule.append((time_, time_ + self.processing_times[i]))
time_ = schedule[-1][1]
return WTSolution(problem=self, schedule=schedule)