job-shop-lib 1.0.2__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. job_shop_lib/__init__.py +1 -1
  2. job_shop_lib/_job_shop_instance.py +26 -26
  3. job_shop_lib/_operation.py +2 -4
  4. job_shop_lib/_schedule.py +11 -11
  5. job_shop_lib/benchmarking/_load_benchmark.py +3 -3
  6. job_shop_lib/constraint_programming/_ortools_solver.py +6 -6
  7. job_shop_lib/dispatching/_dispatcher.py +19 -19
  8. job_shop_lib/dispatching/_dispatcher_observer_config.py +3 -3
  9. job_shop_lib/dispatching/_factories.py +2 -3
  10. job_shop_lib/dispatching/_history_observer.py +1 -2
  11. job_shop_lib/dispatching/_optimal_operations_observer.py +3 -4
  12. job_shop_lib/dispatching/_ready_operation_filters.py +18 -19
  13. job_shop_lib/dispatching/_unscheduled_operations_observer.py +3 -2
  14. job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +10 -12
  15. job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +1 -3
  16. job_shop_lib/dispatching/feature_observers/_factory.py +11 -12
  17. job_shop_lib/dispatching/feature_observers/_feature_observer.py +8 -9
  18. job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +2 -4
  19. job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +2 -4
  20. job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +2 -4
  21. job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +15 -20
  22. job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -10
  23. job_shop_lib/dispatching/rules/_machine_chooser_factory.py +2 -3
  24. job_shop_lib/dispatching/rules/_utils.py +7 -8
  25. job_shop_lib/generation/_general_instance_generator.py +22 -20
  26. job_shop_lib/generation/_instance_generator.py +8 -8
  27. job_shop_lib/generation/_transformations.py +4 -5
  28. job_shop_lib/generation/_utils.py +16 -8
  29. job_shop_lib/graphs/_job_shop_graph.py +13 -14
  30. job_shop_lib/graphs/_node.py +6 -12
  31. job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +2 -4
  32. job_shop_lib/reinforcement_learning/__init__.py +2 -1
  33. job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +17 -17
  34. job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +1 -1
  35. job_shop_lib/reinforcement_learning/_reward_observers.py +1 -3
  36. job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +22 -24
  37. job_shop_lib/reinforcement_learning/_utils.py +2 -2
  38. job_shop_lib/visualization/gantt/_gantt_chart_creator.py +11 -11
  39. job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py +21 -21
  40. job_shop_lib/visualization/gantt/_plot_gantt_chart.py +12 -14
  41. job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +15 -17
  42. job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +22 -24
  43. {job_shop_lib-1.0.2.dist-info → job_shop_lib-1.0.3.dist-info}/METADATA +1 -1
  44. {job_shop_lib-1.0.2.dist-info → job_shop_lib-1.0.3.dist-info}/RECORD +46 -46
  45. {job_shop_lib-1.0.2.dist-info → job_shop_lib-1.0.3.dist-info}/LICENSE +0 -0
  46. {job_shop_lib-1.0.2.dist-info → job_shop_lib-1.0.3.dist-info}/WHEEL +0 -0
job_shop_lib/__init__.py CHANGED
@@ -19,7 +19,7 @@ from job_shop_lib._schedule import Schedule
19
19
  from job_shop_lib._base_solver import BaseSolver, Solver
20
20
 
21
21
 
22
- __version__ = "1.0.2"
22
+ __version__ = "1.0.3"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import functools
7
- from typing import Any, List, Union, Dict
7
+ from typing import Any
8
8
 
9
9
  import numpy as np
10
10
  from numpy.typing import NDArray
@@ -51,14 +51,14 @@ class JobShopInstance:
51
51
  total_duration
52
52
 
53
53
  Attributes:
54
- jobs (List[List[Operation]]):
54
+ jobs (list[list[Operation]]):
55
55
  A list of lists of operations. Each list of operations represents
56
56
  a job, and the operations are ordered by their position in the job.
57
57
  The ``job_id``, ``position_in_job``, and ``operation_id``
58
58
  attributes of the operations are set when the instance is created.
59
59
  name (str):
60
60
  A string with the name of the instance.
61
- metadata (Dict[str, Any]):
61
+ metadata (dict[str, Any]):
62
62
  A dictionary with additional information about the instance.
63
63
 
64
64
  Args:
@@ -81,16 +81,16 @@ class JobShopInstance:
81
81
 
82
82
  def __init__(
83
83
  self,
84
- jobs: List[List[Operation]],
84
+ jobs: list[list[Operation]],
85
85
  name: str = "JobShopInstance",
86
86
  set_operation_attributes: bool = True,
87
87
  **metadata: Any,
88
88
  ):
89
- self.jobs: List[List[Operation]] = jobs
89
+ self.jobs: list[list[Operation]] = jobs
90
90
  if set_operation_attributes:
91
91
  self.set_operation_attributes()
92
92
  self.name: str = name
93
- self.metadata: Dict[str, Any] = metadata
93
+ self.metadata: dict[str, Any] = metadata
94
94
 
95
95
  def set_operation_attributes(self):
96
96
  """Sets the ``job_id``, ``position_in_job``, and ``operation_id``
@@ -125,10 +125,10 @@ class JobShopInstance:
125
125
  @classmethod
126
126
  def from_taillard_file(
127
127
  cls,
128
- file_path: Union[os.PathLike, str, bytes],
128
+ file_path: os.PathLike | str | bytes,
129
129
  encoding: str = "utf-8",
130
130
  comment_symbol: str = "#",
131
- name: Union[str, None] = None,
131
+ name: str | None = None,
132
132
  **metadata: Any,
133
133
  ) -> JobShopInstance:
134
134
  """Creates a JobShopInstance from a file following Taillard's format.
@@ -178,7 +178,7 @@ class JobShopInstance:
178
178
  name = name.split(".")[0]
179
179
  return cls(jobs=jobs, name=name, **metadata)
180
180
 
181
- def to_dict(self) -> Dict[str, Any]:
181
+ def to_dict(self) -> dict[str, Any]:
182
182
  """Returns a dictionary representation of the instance.
183
183
 
184
184
  This representation is useful for saving the instance to a JSON file,
@@ -186,7 +186,7 @@ class JobShopInstance:
186
186
  like Taillard's.
187
187
 
188
188
  Returns:
189
- Dict[str, Any]: The returned dictionary has the following
189
+ dict[str, Any]: The returned dictionary has the following
190
190
  structure:
191
191
 
192
192
  .. code-block:: python
@@ -208,10 +208,10 @@ class JobShopInstance:
208
208
  @classmethod
209
209
  def from_matrices(
210
210
  cls,
211
- duration_matrix: List[List[int]],
212
- machines_matrix: List[List[List[int]]] | List[List[int]],
211
+ duration_matrix: list[list[int]],
212
+ machines_matrix: list[list[list[int]]] | list[list[int]],
213
213
  name: str = "JobShopInstance",
214
- metadata: Dict[str, Any] | None = None,
214
+ metadata: dict[str, Any] | None = None,
215
215
  ) -> JobShopInstance:
216
216
  """Creates a :class:`JobShopInstance` from duration and machines
217
217
  matrices.
@@ -233,7 +233,7 @@ class JobShopInstance:
233
233
  Returns:
234
234
  A :class:`JobShopInstance` object.
235
235
  """
236
- jobs: List[List[Operation]] = [[] for _ in range(len(duration_matrix))]
236
+ jobs: list[list[Operation]] = [[] for _ in range(len(duration_matrix))]
237
237
 
238
238
  num_jobs = len(duration_matrix)
239
239
  for job_id in range(num_jobs):
@@ -290,7 +290,7 @@ class JobShopInstance:
290
290
  )
291
291
 
292
292
  @functools.cached_property
293
- def durations_matrix(self) -> List[List[int]]:
293
+ def durations_matrix(self) -> list[list[int]]:
294
294
  """Returns the duration matrix of the instance.
295
295
 
296
296
  The duration of the operation with ``job_id`` i and ``position_in_job``
@@ -305,7 +305,7 @@ class JobShopInstance:
305
305
  return [[operation.duration for operation in job] for job in self.jobs]
306
306
 
307
307
  @functools.cached_property
308
- def machines_matrix(self) -> Union[List[List[List[int]]], List[List[int]]]:
308
+ def machines_matrix(self) -> list[list[list[int]]] | list[list[int]]:
309
309
  """Returns the machines matrix of the instance.
310
310
 
311
311
  If the instance is flexible (i.e., if any operation has more than one
@@ -371,25 +371,25 @@ class JobShopInstance:
371
371
  machines_matrix = self.machines_matrix
372
372
  if self.is_flexible:
373
373
  # False positive from mypy, the type of machines_matrix is
374
- # List[List[List[int]]] here
374
+ # list[list[list[int]]] here
375
375
  return self._fill_matrix_with_nans_3d(
376
376
  machines_matrix # type: ignore[arg-type]
377
377
  )
378
378
 
379
379
  # False positive from mypy, the type of machines_matrix is
380
- # List[List[int]] here
380
+ # list[list[int]] here
381
381
  return self._fill_matrix_with_nans_2d(
382
382
  machines_matrix # type: ignore[arg-type]
383
383
  )
384
384
 
385
385
  @functools.cached_property
386
- def operations_by_machine(self) -> List[List[Operation]]:
386
+ def operations_by_machine(self) -> list[list[Operation]]:
387
387
  """Returns a list of lists of operations.
388
388
 
389
389
  The i-th list contains the operations that can be processed in the
390
390
  machine with id i.
391
391
  """
392
- operations_by_machine: List[List[Operation]] = [
392
+ operations_by_machine: list[list[Operation]] = [
393
393
  [] for _ in range(self.num_machines)
394
394
  ]
395
395
  for job in self.jobs:
@@ -409,7 +409,7 @@ class JobShopInstance:
409
409
  )
410
410
 
411
411
  @functools.cached_property
412
- def max_duration_per_job(self) -> List[float]:
412
+ def max_duration_per_job(self) -> list[float]:
413
413
  """Returns the maximum duration of each job in the instance.
414
414
 
415
415
  The maximum duration of the job with id i is stored in the i-th
@@ -420,7 +420,7 @@ class JobShopInstance:
420
420
  return [max(op.duration for op in job) for job in self.jobs]
421
421
 
422
422
  @functools.cached_property
423
- def max_duration_per_machine(self) -> List[int]:
423
+ def max_duration_per_machine(self) -> list[int]:
424
424
  """Returns the maximum duration of each machine in the instance.
425
425
 
426
426
  The maximum duration of the machine with id i is stored in the i-th
@@ -439,7 +439,7 @@ class JobShopInstance:
439
439
  return max_duration_per_machine
440
440
 
441
441
  @functools.cached_property
442
- def job_durations(self) -> List[int]:
442
+ def job_durations(self) -> list[int]:
443
443
  """Returns a list with the duration of each job in the instance.
444
444
 
445
445
  The duration of a job is the sum of the durations of its operations.
@@ -450,7 +450,7 @@ class JobShopInstance:
450
450
  return [sum(op.duration for op in job) for job in self.jobs]
451
451
 
452
452
  @functools.cached_property
453
- def machine_loads(self) -> List[int]:
453
+ def machine_loads(self) -> list[int]:
454
454
  """Returns the total machine load of each machine in the instance.
455
455
 
456
456
  The total machine load of a machine is the sum of the durations of the
@@ -474,7 +474,7 @@ class JobShopInstance:
474
474
 
475
475
  @staticmethod
476
476
  def _fill_matrix_with_nans_2d(
477
- matrix: List[List[int]],
477
+ matrix: list[list[int]],
478
478
  ) -> NDArray[np.float32]:
479
479
  """Fills a matrix with ``np.nan`` values.
480
480
 
@@ -496,7 +496,7 @@ class JobShopInstance:
496
496
 
497
497
  @staticmethod
498
498
  def _fill_matrix_with_nans_3d(
499
- matrix: List[List[List[int]]],
499
+ matrix: list[list[list[int]]],
500
500
  ) -> NDArray[np.float32]:
501
501
  """Fills a 3D matrix with ``np.nan`` values.
502
502
 
@@ -2,8 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Union, List
6
-
7
5
  from job_shop_lib.exceptions import ValidationError
8
6
 
9
7
 
@@ -61,8 +59,8 @@ class Operation:
61
59
  ),
62
60
  }
63
61
 
64
- def __init__(self, machines: Union[int, List[int]], duration: int):
65
- self.machines: List[int] = (
62
+ def __init__(self, machines: int | list[int], duration: int):
63
+ self.machines: list[int] = (
66
64
  [machines] if isinstance(machines, int) else machines
67
65
  )
68
66
  self.duration: int = duration
job_shop_lib/_schedule.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, List, Union, Dict, Optional
5
+ from typing import Any
6
6
  from collections import deque
7
7
 
8
8
  from job_shop_lib import ScheduledOperation, JobShopInstance
@@ -55,7 +55,7 @@ class Schedule:
55
55
  def __init__(
56
56
  self,
57
57
  instance: JobShopInstance,
58
- schedule: Optional[List[List[ScheduledOperation]]] = None,
58
+ schedule: list[list[ScheduledOperation]] | None = None,
59
59
  **metadata: Any,
60
60
  ):
61
61
  if schedule is None:
@@ -65,19 +65,19 @@ class Schedule:
65
65
 
66
66
  self.instance: JobShopInstance = instance
67
67
  self._schedule = schedule
68
- self.metadata: Dict[str, Any] = metadata
68
+ self.metadata: dict[str, Any] = metadata
69
69
 
70
70
  def __repr__(self) -> str:
71
71
  return str(self.schedule)
72
72
 
73
73
  @property
74
- def schedule(self) -> List[List[ScheduledOperation]]:
74
+ def schedule(self) -> list[list[ScheduledOperation]]:
75
75
  """A list of lists of :class:`ScheduledOperation` objects. Each list
76
76
  represents the order of operations on a machine."""
77
77
  return self._schedule
78
78
 
79
79
  @schedule.setter
80
- def schedule(self, new_schedule: List[List[ScheduledOperation]]):
80
+ def schedule(self, new_schedule: list[list[ScheduledOperation]]):
81
81
  Schedule.check_schedule(new_schedule)
82
82
  self._schedule = new_schedule
83
83
 
@@ -103,7 +103,7 @@ class Schedule:
103
103
  - **"metadata"**: A dictionary with additional information
104
104
  about the schedule.
105
105
  """
106
- job_sequences: List[List[int]] = []
106
+ job_sequences: list[list[int]] = []
107
107
  for machine_schedule in self.schedule:
108
108
  job_sequences.append(
109
109
  [operation.job_id for operation in machine_schedule]
@@ -117,9 +117,9 @@ class Schedule:
117
117
 
118
118
  @staticmethod
119
119
  def from_dict(
120
- instance: Union[Dict[str, Any], JobShopInstance],
121
- job_sequences: List[List[int]],
122
- metadata: Optional[Dict[str, Any]] = None,
120
+ instance: dict[str, Any] | JobShopInstance,
121
+ job_sequences: list[list[int]],
122
+ metadata: dict[str, Any] | None = None,
123
123
  ) -> Schedule:
124
124
  """Creates a schedule from a dictionary representation."""
125
125
  if isinstance(instance, dict):
@@ -131,7 +131,7 @@ class Schedule:
131
131
  @staticmethod
132
132
  def from_job_sequences(
133
133
  instance: JobShopInstance,
134
- job_sequences: List[List[int]],
134
+ job_sequences: list[list[int]],
135
135
  ) -> Schedule:
136
136
  """Creates an active schedule from a list of job sequences.
137
137
 
@@ -240,7 +240,7 @@ class Schedule:
240
240
  return previous_operation.end_time <= scheduled_operation.start_time
241
241
 
242
242
  @staticmethod
243
- def check_schedule(schedule: List[List[ScheduledOperation]]):
243
+ def check_schedule(schedule: list[list[ScheduledOperation]]):
244
244
  """Checks if a schedule is valid and raises a
245
245
  :class:`~exceptions.ValidationError` if it is not.
246
246
 
@@ -1,6 +1,6 @@
1
1
  """Contains functions to load benchmark instances from a JSON file."""
2
2
 
3
- from typing import Any, Dict
3
+ from typing import Any
4
4
 
5
5
  import functools
6
6
  import json
@@ -10,7 +10,7 @@ from job_shop_lib import JobShopInstance
10
10
 
11
11
 
12
12
  @functools.cache
13
- def load_all_benchmark_instances() -> Dict[str, JobShopInstance]:
13
+ def load_all_benchmark_instances() -> dict[str, JobShopInstance]:
14
14
  """Loads all benchmark instances available.
15
15
 
16
16
  Returns:
@@ -48,7 +48,7 @@ def load_benchmark_instance(name: str) -> JobShopInstance:
48
48
 
49
49
 
50
50
  @functools.cache
51
- def load_benchmark_json() -> Dict[str, Dict[str, Any]]:
51
+ def load_benchmark_json() -> dict[str, dict[str, Any]]:
52
52
  """Loads the raw JSON file containing the benchmark instances.
53
53
 
54
54
  Results are cached to avoid reading the file multiple times.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Dict, List, Tuple
5
+ from typing import Any
6
6
  import time
7
7
 
8
8
  from ortools.sat.python import cp_model
@@ -62,7 +62,7 @@ class ORToolsSolver(BaseSolver):
62
62
  self._makespan: cp_model.IntVar | None = None
63
63
  self.model = cp_model.CpModel()
64
64
  self.solver = cp_model.CpSolver()
65
- self._operations_start: Dict[Operation, Tuple[IntVar, IntVar]] = {}
65
+ self._operations_start: dict[Operation, tuple[IntVar, IntVar]] = {}
66
66
 
67
67
  def __call__(self, instance: JobShopInstance) -> Schedule:
68
68
  """Equivalent to calling the :meth:`~ORToolsSolver.solve` method.
@@ -152,15 +152,15 @@ class ORToolsSolver(BaseSolver):
152
152
  self._set_objective(instance)
153
153
 
154
154
  def _create_schedule(
155
- self, instance: JobShopInstance, metadata: Dict[str, Any]
155
+ self, instance: JobShopInstance, metadata: dict[str, Any]
156
156
  ) -> Schedule:
157
157
  """Creates a Schedule object from the solution."""
158
- operations_start: Dict[Operation, int] = {
158
+ operations_start: dict[Operation, int] = {
159
159
  operation: self.solver.Value(start_var)
160
160
  for operation, (start_var, _) in self._operations_start.items()
161
161
  }
162
162
 
163
- unsorted_schedule: List[List[ScheduledOperation]] = [
163
+ unsorted_schedule: list[list[ScheduledOperation]] = [
164
164
  [] for _ in range(instance.num_machines)
165
165
  ]
166
166
  for operation, start_time in operations_start.items():
@@ -235,7 +235,7 @@ class ORToolsSolver(BaseSolver):
235
235
  each machine."""
236
236
 
237
237
  # Create interval variables for each operation on each machine
238
- machines_operations: List[List[Tuple[Tuple[IntVar, IntVar], int]]] = [
238
+ machines_operations: list[list[tuple[tuple[IntVar, IntVar], int]]] = [
239
239
  [] for _ in range(instance.num_machines)
240
240
  ]
241
241
  for job in instance.jobs:
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import abc
6
- from typing import Any, TypeVar, List, Optional, Type, Set
6
+ from typing import Any, TypeVar
7
7
  from collections.abc import Callable
8
8
  from functools import wraps
9
9
 
@@ -52,7 +52,7 @@ class DispatcherObserver(abc.ABC):
52
52
  class HistoryObserver(DispatcherObserver):
53
53
  def __init__(self, dispatcher: Dispatcher):
54
54
  super().__init__(dispatcher)
55
- self.history: List[ScheduledOperation] = []
55
+ self.history: list[ScheduledOperation] = []
56
56
 
57
57
  def update(self, scheduled_operation: ScheduledOperation):
58
58
  self.history.append(scheduled_operation)
@@ -212,14 +212,14 @@ class Dispatcher:
212
212
  self,
213
213
  instance: JobShopInstance,
214
214
  ready_operations_filter: (
215
- Optional[Callable[[Dispatcher, List[Operation]], List[Operation]]]
215
+ Callable[[Dispatcher, list[Operation]], list[Operation]] | None
216
216
  ) = None,
217
217
  ) -> None:
218
218
 
219
219
  self.instance = instance
220
220
  self.schedule = Schedule(self.instance)
221
221
  self.ready_operations_filter = ready_operations_filter
222
- self.subscribers: List[DispatcherObserver] = []
222
+ self.subscribers: list[DispatcherObserver] = []
223
223
 
224
224
  self._machine_next_available_time = [0] * self.instance.num_machines
225
225
  self._job_next_operation_index = [0] * self.instance.num_jobs
@@ -233,18 +233,18 @@ class Dispatcher:
233
233
  return str(self)
234
234
 
235
235
  @property
236
- def machine_next_available_time(self) -> List[int]:
236
+ def machine_next_available_time(self) -> list[int]:
237
237
  """Returns the next available time for each machine."""
238
238
  return self._machine_next_available_time
239
239
 
240
240
  @property
241
- def job_next_operation_index(self) -> List[int]:
241
+ def job_next_operation_index(self) -> list[int]:
242
242
  """Returns the index of the next operation to be scheduled for each
243
243
  job."""
244
244
  return self._job_next_operation_index
245
245
 
246
246
  @property
247
- def job_next_available_time(self) -> List[int]:
247
+ def job_next_available_time(self) -> list[int]:
248
248
  """Returns the next available time for each job."""
249
249
  return self._job_next_available_time
250
250
 
@@ -267,7 +267,7 @@ class Dispatcher:
267
267
  subscriber.reset()
268
268
 
269
269
  def dispatch(
270
- self, operation: Operation, machine_id: Optional[int] = None
270
+ self, operation: Operation, machine_id: int | None = None
271
271
  ) -> None:
272
272
  """Schedules the given operation on the given machine.
273
273
 
@@ -362,7 +362,7 @@ class Dispatcher:
362
362
 
363
363
  def create_or_get_observer(
364
364
  self,
365
- observer: Type[ObserverType],
365
+ observer: type[ObserverType],
366
366
  condition: Callable[[DispatcherObserver], bool] = lambda _: True,
367
367
  **kwargs,
368
368
  ) -> ObserverType:
@@ -401,7 +401,7 @@ class Dispatcher:
401
401
  current_time = self.min_start_time(available_operations)
402
402
  return current_time
403
403
 
404
- def min_start_time(self, operations: List[Operation]) -> int:
404
+ def min_start_time(self, operations: list[Operation]) -> int:
405
405
  """Returns the minimum start time of the available operations."""
406
406
  if not operations:
407
407
  return self.schedule.makespan()
@@ -413,7 +413,7 @@ class Dispatcher:
413
413
  return int(min_start_time)
414
414
 
415
415
  @_dispatcher_cache
416
- def available_operations(self) -> List[Operation]:
416
+ def available_operations(self) -> list[Operation]:
417
417
  """Returns a list of available operations for processing, optionally
418
418
  filtering out operations using the filter function.
419
419
 
@@ -433,7 +433,7 @@ class Dispatcher:
433
433
  return available_operations
434
434
 
435
435
  @_dispatcher_cache
436
- def raw_ready_operations(self) -> List[Operation]:
436
+ def raw_ready_operations(self) -> list[Operation]:
437
437
  """Returns a list of available operations for processing without
438
438
  applying the filter function.
439
439
 
@@ -449,7 +449,7 @@ class Dispatcher:
449
449
  return available_operations
450
450
 
451
451
  @_dispatcher_cache
452
- def unscheduled_operations(self) -> List[Operation]:
452
+ def unscheduled_operations(self) -> list[Operation]:
453
453
  """Returns the list of operations that have not been scheduled."""
454
454
  unscheduled_operations = []
455
455
  for job_id, next_position in enumerate(self._job_next_operation_index):
@@ -458,7 +458,7 @@ class Dispatcher:
458
458
  return unscheduled_operations
459
459
 
460
460
  @_dispatcher_cache
461
- def scheduled_operations(self) -> List[ScheduledOperation]:
461
+ def scheduled_operations(self) -> list[ScheduledOperation]:
462
462
  """Returns the list of operations that have been scheduled."""
463
463
  scheduled_operations = []
464
464
  for machine_schedule in self.schedule.schedule:
@@ -466,7 +466,7 @@ class Dispatcher:
466
466
  return scheduled_operations
467
467
 
468
468
  @_dispatcher_cache
469
- def available_machines(self) -> List[int]:
469
+ def available_machines(self) -> list[int]:
470
470
  """Returns the list of ready machines."""
471
471
  available_operations = self.available_operations()
472
472
  available_machines = set()
@@ -475,7 +475,7 @@ class Dispatcher:
475
475
  return list(available_machines)
476
476
 
477
477
  @_dispatcher_cache
478
- def available_jobs(self) -> List[int]:
478
+ def available_jobs(self) -> list[int]:
479
479
  """Returns the list of ready jobs."""
480
480
  available_operations = self.available_operations()
481
481
  available_jobs = set(
@@ -530,7 +530,7 @@ class Dispatcher:
530
530
  return scheduled_operation.end_time - adjusted_start_time
531
531
 
532
532
  @_dispatcher_cache
533
- def completed_operations(self) -> Set[ScheduledOperation]:
533
+ def completed_operations(self) -> set[ScheduledOperation]:
534
534
  """Returns the set of operations that have been completed.
535
535
 
536
536
  This method returns the operations that have been scheduled and the
@@ -542,7 +542,7 @@ class Dispatcher:
542
542
  return completed_operations
543
543
 
544
544
  @_dispatcher_cache
545
- def uncompleted_operations(self) -> List[Operation]:
545
+ def uncompleted_operations(self) -> list[Operation]:
546
546
  """Returns the list of operations that have not been completed yet.
547
547
 
548
548
  This method checks for operations that either haven't been scheduled
@@ -561,7 +561,7 @@ class Dispatcher:
561
561
  return uncompleted_operations
562
562
 
563
563
  @_dispatcher_cache
564
- def ongoing_operations(self) -> List[ScheduledOperation]:
564
+ def ongoing_operations(self) -> list[ScheduledOperation]:
565
565
  """Returns the list of operations that are currently being processed.
566
566
 
567
567
  This method returns the operations that have been scheduled and are
@@ -5,7 +5,7 @@ The factory functions create and return the appropriate functions based on the
5
5
  specified names or enums.
6
6
  """
7
7
 
8
- from typing import TypeVar, Generic, Any, Dict
8
+ from typing import TypeVar, Generic, Any
9
9
 
10
10
  from dataclasses import dataclass, field
11
11
 
@@ -46,7 +46,7 @@ class DispatcherObserverConfig(Generic[T]):
46
46
  # We use the type hint T, instead of ObserverType, to allow for string or
47
47
  # specific Enum values to be passed as the type argument. For example:
48
48
  # FeatureObserverConfig = DispatcherObserverConfig[
49
- # Type[FeatureObserver] | FeatureObserverType | str
49
+ # type[FeatureObserver] | FeatureObserverType | str
50
50
  # ]
51
51
  # This allows for the creation of a FeatureObserver instance
52
52
  # from the factory function.
@@ -55,7 +55,7 @@ class DispatcherObserverConfig(Generic[T]):
55
55
  enum value, or a string. This is useful for the creation of
56
56
  :class:`DispatcherObserver` instances from the factory functions."""
57
57
 
58
- kwargs: Dict[str, Any] = field(default_factory=dict)
58
+ kwargs: dict[str, Any] = field(default_factory=dict)
59
59
  """Keyword arguments needed to initialize the class. It must not
60
60
  contain the ``dispatcher`` argument."""
61
61
 
@@ -4,7 +4,6 @@ The factory functions create and return the appropriate functions based on the
4
4
  specified names or enums.
5
5
  """
6
6
 
7
- from typing import Union
8
7
  from enum import Enum
9
8
  from collections.abc import Iterable
10
9
 
@@ -37,7 +36,7 @@ class ReadyOperationsFilterType(str, Enum):
37
36
 
38
37
  def create_composite_operation_filter(
39
38
  ready_operations_filters: Iterable[
40
- Union[ReadyOperationsFilter, str, ReadyOperationsFilterType]
39
+ ReadyOperationsFilter | str | ReadyOperationsFilterType
41
40
  ],
42
41
  ) -> ReadyOperationsFilter:
43
42
  """Creates and returns a :class:`ReadyOperationsFilter` function by
@@ -85,7 +84,7 @@ def create_composite_operation_filter(
85
84
 
86
85
 
87
86
  def ready_operations_filter_factory(
88
- filter_name: Union[str, ReadyOperationsFilterType, ReadyOperationsFilter]
87
+ filter_name: str | ReadyOperationsFilterType | ReadyOperationsFilter,
89
88
  ) -> ReadyOperationsFilter:
90
89
  """Creates and returns a filter function based on the specified
91
90
  filter strategy name.
@@ -1,6 +1,5 @@
1
1
  """Home of the `HistoryObserver` class."""
2
2
 
3
- from typing import List
4
3
  from job_shop_lib.dispatching import DispatcherObserver, Dispatcher
5
4
  from job_shop_lib import ScheduledOperation
6
5
 
@@ -10,7 +9,7 @@ class HistoryObserver(DispatcherObserver):
10
9
 
11
10
  def __init__(self, dispatcher: Dispatcher, *, subscribe: bool = True):
12
11
  super().__init__(dispatcher, subscribe=subscribe)
13
- self.history: List[ScheduledOperation] = []
12
+ self.history: list[ScheduledOperation] = []
14
13
 
15
14
  def update(self, scheduled_operation: ScheduledOperation):
16
15
  self.history.append(scheduled_operation)
@@ -1,6 +1,5 @@
1
1
  """Home of the `OptimalOperationsObserver` class."""
2
2
 
3
- from typing import List, Set, Dict
4
3
  from job_shop_lib.dispatching import DispatcherObserver, Dispatcher
5
4
  from job_shop_lib import Schedule, Operation, ScheduledOperation
6
5
  from job_shop_lib.exceptions import ValidationError
@@ -53,9 +52,9 @@ class OptimalOperationsObserver(DispatcherObserver):
53
52
  )
54
53
 
55
54
  self.reference_schedule = reference_schedule
56
- self.optimal_available: Set[Operation] = set()
57
- self._operation_to_scheduled: Dict[Operation, ScheduledOperation] = {}
58
- self._machine_next_operation_index: List[int] = [0] * len(
55
+ self.optimal_available: set[Operation] = set()
56
+ self._operation_to_scheduled: dict[Operation, ScheduledOperation] = {}
57
+ self._machine_next_operation_index: list[int] = [0] * len(
59
58
  reference_schedule.schedule
60
59
  )
61
60