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
@@ -4,7 +4,6 @@ This functions are used by the `Dispatcher` class to reduce the
4
4
  amount of available operations to choose from.
5
5
  """
6
6
 
7
- from typing import List, Set
8
7
  from collections.abc import Callable
9
8
 
10
9
  from job_shop_lib import Operation
@@ -12,13 +11,13 @@ from job_shop_lib.dispatching import Dispatcher
12
11
 
13
12
 
14
13
  ReadyOperationsFilter = Callable[
15
- [Dispatcher, List[Operation]], List[Operation]
14
+ [Dispatcher, list[Operation]], list[Operation]
16
15
  ]
17
16
 
18
17
 
19
18
  def filter_non_idle_machines(
20
- dispatcher: Dispatcher, operations: List[Operation]
21
- ) -> List[Operation]:
19
+ dispatcher: Dispatcher, operations: list[Operation]
20
+ ) -> list[Operation]:
22
21
  """Filters out all the operations associated with non-idle machines.
23
22
 
24
23
  A machine is considered idle if there are no ongoing operations
@@ -41,7 +40,7 @@ def filter_non_idle_machines(
41
40
 
42
41
  # Filter operations to keep those that are associated with at least one
43
42
  # idle machine
44
- filtered_operations: List[Operation] = []
43
+ filtered_operations: list[Operation] = []
45
44
  for operation in operations:
46
45
  if all(
47
46
  machine_id in non_idle_machines
@@ -55,7 +54,7 @@ def filter_non_idle_machines(
55
54
 
56
55
  def _get_non_idle_machines(
57
56
  dispatcher: Dispatcher, current_time: int
58
- ) -> Set[int]:
57
+ ) -> set[int]:
59
58
  """Returns the set of machine ids that are currently busy (i.e., have at
60
59
  least one uncompleted operation)."""
61
60
 
@@ -71,8 +70,8 @@ def _get_non_idle_machines(
71
70
 
72
71
 
73
72
  def filter_non_immediate_operations(
74
- dispatcher: Dispatcher, operations: List[Operation]
75
- ) -> List[Operation]:
73
+ dispatcher: Dispatcher, operations: list[Operation]
74
+ ) -> list[Operation]:
76
75
  """Filters out all the operations that can't start immediately.
77
76
 
78
77
  An operation can start immediately if its earliest start time is the
@@ -87,7 +86,7 @@ def filter_non_immediate_operations(
87
86
  """
88
87
 
89
88
  min_start_time = dispatcher.min_start_time(operations)
90
- immediate_operations: List[Operation] = []
89
+ immediate_operations: list[Operation] = []
91
90
  for operation in operations:
92
91
  start_time = dispatcher.earliest_start_time(operation)
93
92
  if start_time == min_start_time:
@@ -97,8 +96,8 @@ def filter_non_immediate_operations(
97
96
 
98
97
 
99
98
  def filter_dominated_operations(
100
- dispatcher: Dispatcher, operations: List[Operation]
101
- ) -> List[Operation]:
99
+ dispatcher: Dispatcher, operations: list[Operation]
100
+ ) -> list[Operation]:
102
101
  """Filters out all the operations that are dominated.
103
102
  An operation is dominated if there is another operation that ends before
104
103
  it starts on the same machine.
@@ -106,7 +105,7 @@ def filter_dominated_operations(
106
105
 
107
106
  min_machine_end_times = _get_min_machine_end_times(dispatcher, operations)
108
107
 
109
- non_dominated_operations: List[Operation] = []
108
+ non_dominated_operations: list[Operation] = []
110
109
  for operation in operations:
111
110
  # One benchmark instance has an operation with duration 0
112
111
  if operation.duration == 0:
@@ -122,13 +121,13 @@ def filter_dominated_operations(
122
121
 
123
122
 
124
123
  def filter_non_immediate_machines(
125
- dispatcher: Dispatcher, operations: List[Operation]
126
- ) -> List[Operation]:
124
+ dispatcher: Dispatcher, operations: list[Operation]
125
+ ) -> list[Operation]:
127
126
  """Filters out all the operations associated with machines which earliest
128
127
  operation is not the current time."""
129
128
 
130
129
  is_immediate_machine = _get_immediate_machines(dispatcher, operations)
131
- non_dominated_operations: List[Operation] = []
130
+ non_dominated_operations: list[Operation] = []
132
131
  for operation in operations:
133
132
  if any(
134
133
  is_immediate_machine[machine_id]
@@ -140,8 +139,8 @@ def filter_non_immediate_machines(
140
139
 
141
140
 
142
141
  def _get_min_machine_end_times(
143
- dispatcher: Dispatcher, available_operations: List[Operation]
144
- ) -> List[float]:
142
+ dispatcher: Dispatcher, available_operations: list[Operation]
143
+ ) -> list[float]:
145
144
  end_times_per_machine = [float("inf")] * dispatcher.instance.num_machines
146
145
  for op in available_operations:
147
146
  for machine_id in op.machines:
@@ -153,8 +152,8 @@ def _get_min_machine_end_times(
153
152
 
154
153
 
155
154
  def _get_immediate_machines(
156
- dispatcher: Dispatcher, available_operations: List[Operation]
157
- ) -> List[bool]:
155
+ dispatcher: Dispatcher, available_operations: list[Operation]
156
+ ) -> list[bool]:
158
157
  """Returns the machine ids of the machines that have at least one
159
158
  operation with the lowest start time (i.e. the start time)."""
160
159
  working_machines = [False] * dispatcher.instance.num_machines
@@ -3,7 +3,6 @@
3
3
  import collections
4
4
  from collections.abc import Iterable
5
5
  import itertools
6
- from typing import Deque, List
7
6
 
8
7
  from job_shop_lib import Operation, ScheduledOperation
9
8
  from job_shop_lib.dispatching import DispatcherObserver, Dispatcher
@@ -19,7 +18,9 @@ class UnscheduledOperationsObserver(DispatcherObserver):
19
18
 
20
19
  def __init__(self, dispatcher: Dispatcher, *, subscribe: bool = True):
21
20
  super().__init__(dispatcher, subscribe=subscribe)
22
- self.unscheduled_operations_per_job: List[Deque[Operation]] = []
21
+ self.unscheduled_operations_per_job: list[
22
+ collections.deque[Operation]
23
+ ] = []
23
24
  self.reset()
24
25
  # In case the dispatcher has already scheduled some operations,
25
26
  # we need to remove them.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from collections import defaultdict
4
4
  from collections.abc import Sequence
5
- from typing import List, Dict, Union, Optional, Type
5
+
6
6
  # The Self type can be imported directly from Python’s typing module in
7
7
  # version 3.11 and beyond. We use the typing_extensions module to support
8
8
  # python >=3.10
@@ -77,8 +77,8 @@ class CompositeFeatureObserver(FeatureObserver):
77
77
  dispatcher: Dispatcher,
78
78
  *,
79
79
  subscribe: bool = True,
80
- feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
81
- feature_observers: Optional[List[FeatureObserver]] = None,
80
+ feature_types: list[FeatureType] | FeatureType | None = None,
81
+ feature_observers: list[FeatureObserver] | None = None,
82
82
  ):
83
83
  if feature_observers is None:
84
84
  feature_observers = [
@@ -97,7 +97,7 @@ class CompositeFeatureObserver(FeatureObserver):
97
97
  f"Composite feature types: {feature_types}"
98
98
  )
99
99
  self.feature_observers = feature_observers
100
- self.column_names: Dict[FeatureType, List[str]] = defaultdict(list)
100
+ self.column_names: dict[FeatureType, list[str]] = defaultdict(list)
101
101
  super().__init__(dispatcher, subscribe=subscribe)
102
102
  self._set_column_names()
103
103
 
@@ -106,12 +106,10 @@ class CompositeFeatureObserver(FeatureObserver):
106
106
  cls,
107
107
  dispatcher: Dispatcher,
108
108
  feature_observer_configs: Sequence[
109
- Union[
110
- str,
111
- FeatureObserverType,
112
- Type[FeatureObserver],
113
- FeatureObserverConfig,
114
- ],
109
+ str
110
+ | FeatureObserverType
111
+ | type[FeatureObserver]
112
+ | FeatureObserverConfig
115
113
  ],
116
114
  subscribe: bool = True,
117
115
  ) -> Self:
@@ -136,7 +134,7 @@ class CompositeFeatureObserver(FeatureObserver):
136
134
  return composite_observer
137
135
 
138
136
  @property
139
- def features_as_dataframe(self) -> Dict[FeatureType, pd.DataFrame]:
137
+ def features_as_dataframe(self) -> dict[FeatureType, pd.DataFrame]:
140
138
  """Returns the features as a dictionary of `pd.DataFrame` instances."""
141
139
  return {
142
140
  feature_type: pd.DataFrame(
@@ -146,7 +144,7 @@ class CompositeFeatureObserver(FeatureObserver):
146
144
  }
147
145
 
148
146
  def initialize_features(self):
149
- features: Dict[FeatureType, List[NDArray[np.float32]]] = defaultdict(
147
+ features: dict[FeatureType, list[NDArray[np.float32]]] = defaultdict(
150
148
  list
151
149
  )
152
150
  for observer in self.feature_observers:
@@ -1,7 +1,5 @@
1
1
  """Home of the `EarliestStartTimeObserver` class."""
2
2
 
3
- from typing import List, Optional, Union
4
-
5
3
  import numpy as np
6
4
  from numpy.typing import NDArray
7
5
 
@@ -77,7 +75,7 @@ class EarliestStartTimeObserver(FeatureObserver):
77
75
  dispatcher: Dispatcher,
78
76
  *,
79
77
  subscribe: bool = True,
80
- feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
78
+ feature_types: list[FeatureType] | FeatureType | None = None,
81
79
  ):
82
80
 
83
81
  # Earliest start times initialization
@@ -1,7 +1,6 @@
1
1
  """Contains factory functions for creating :class:`FeatureObserver`s."""
2
2
 
3
3
  from enum import Enum
4
- from typing import Union, Type
5
4
 
6
5
  from job_shop_lib.dispatching import DispatcherObserverConfig
7
6
  from job_shop_lib.dispatching.feature_observers import (
@@ -37,25 +36,25 @@ class FeatureObserverType(str, Enum):
37
36
 
38
37
 
39
38
  # FeatureObserverConfig = DispatcherObserverConfig[
40
- # Type[FeatureObserver] | FeatureObserverType | str
39
+ # type[FeatureObserver] | FeatureObserverType | str
41
40
  # ]
42
41
  # FeatureObserverConfig = DispatcherObserverConfig[
43
- # Union[Type[FeatureObserver], FeatureObserverType, str]
42
+ # Union[type[FeatureObserver], FeatureObserverType, str]
44
43
  # ]
45
44
  FeatureObserverConfig = (
46
- DispatcherObserverConfig[Type[FeatureObserver]]
45
+ DispatcherObserverConfig[type[FeatureObserver]]
47
46
  | DispatcherObserverConfig[FeatureObserverType]
48
47
  | DispatcherObserverConfig[str]
49
48
  )
50
49
 
51
50
 
52
51
  def feature_observer_factory(
53
- feature_observer_type: Union[
54
- str,
55
- FeatureObserverType,
56
- Type[FeatureObserver],
57
- FeatureObserverConfig
58
- ],
52
+ feature_observer_type: (
53
+ str
54
+ | FeatureObserverType
55
+ | type[FeatureObserver]
56
+ | FeatureObserverConfig
57
+ ),
59
58
  **kwargs,
60
59
  ) -> FeatureObserver:
61
60
  """Creates and returns a :class:`FeatureObserver` based on the specified
@@ -77,12 +76,12 @@ def feature_observer_factory(
77
76
  **feature_observer_type.kwargs,
78
77
  **kwargs,
79
78
  )
80
- # if the instance is of type Type[FeatureObserver] we can just
79
+ # if the instance is of type type[FeatureObserver] we can just
81
80
  # call the object constructor with the keyword arguments
82
81
  if isinstance(feature_observer_type, type):
83
82
  return feature_observer_type(**kwargs)
84
83
 
85
- mapping: dict[FeatureObserverType, Type[FeatureObserver]] = {
84
+ mapping: dict[FeatureObserverType, type[FeatureObserver]] = {
86
85
  FeatureObserverType.IS_READY: IsReadyObserver,
87
86
  FeatureObserverType.EARLIEST_START_TIME: EarliestStartTimeObserver,
88
87
  FeatureObserverType.DURATION: DurationObserver,
@@ -1,7 +1,6 @@
1
1
  """Home of the `FeatureObserver` class and `FeatureType` enum."""
2
2
 
3
3
  import enum
4
- from typing import Optional, Union, Dict, List, Tuple
5
4
 
6
5
  import numpy as np
7
6
 
@@ -65,7 +64,7 @@ class FeatureObserver(DispatcherObserver):
65
64
  """
66
65
 
67
66
  _is_singleton = False
68
- _feature_sizes: Union[Dict[FeatureType, int], int] = 1
67
+ _feature_sizes: dict[FeatureType, int] | int = 1
69
68
  _supported_feature_types = list(FeatureType)
70
69
 
71
70
  __slots__ = {
@@ -85,7 +84,7 @@ class FeatureObserver(DispatcherObserver):
85
84
  dispatcher: Dispatcher,
86
85
  *,
87
86
  subscribe: bool = True,
88
- feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
87
+ feature_types: list[FeatureType] | FeatureType | None = None,
89
88
  ):
90
89
  feature_types = self._get_feature_types_list(feature_types)
91
90
  if isinstance(self._feature_sizes, int):
@@ -117,7 +116,7 @@ class FeatureObserver(DispatcherObserver):
117
116
  self.initialize_features()
118
117
 
119
118
  @property
120
- def feature_sizes(self) -> Dict[FeatureType, int]:
119
+ def feature_sizes(self) -> dict[FeatureType, int]:
121
120
  """Returns the size of the features.
122
121
 
123
122
  The size of the features is the number of values being observed for
@@ -135,12 +134,12 @@ class FeatureObserver(DispatcherObserver):
135
134
  return self._feature_sizes
136
135
 
137
136
  @property
138
- def supported_feature_types(self) -> List[FeatureType]:
137
+ def supported_feature_types(self) -> list[FeatureType]:
139
138
  """Returns the supported feature types."""
140
139
  return self._supported_feature_types
141
140
 
142
141
  @property
143
- def feature_dimensions(self) -> Dict[FeatureType, Tuple[int, int]]:
142
+ def feature_dimensions(self) -> dict[FeatureType, tuple[int, int]]:
144
143
  """A dictionary containing the shape of each :class:`FeatureType`."""
145
144
  feature_dimensions = {}
146
145
  for feature_type, array in self.features.items():
@@ -171,7 +170,7 @@ class FeatureObserver(DispatcherObserver):
171
170
  self.initialize_features()
172
171
 
173
172
  def set_features_to_zero(
174
- self, exclude: Optional[Union[FeatureType, List[FeatureType]]] = None
173
+ self, exclude: FeatureType | list[FeatureType] | None = None
175
174
  ):
176
175
  """Sets all features to zero except for the ones specified in
177
176
  ``exclude``.
@@ -197,8 +196,8 @@ class FeatureObserver(DispatcherObserver):
197
196
 
198
197
  def _get_feature_types_list(
199
198
  self,
200
- feature_types: Optional[Union[List[FeatureType], FeatureType]],
201
- ) -> List[FeatureType]:
199
+ feature_types: list[FeatureType] | FeatureType | None,
200
+ ) -> list[FeatureType]:
202
201
  """Returns a list of feature types.
203
202
 
204
203
  Args:
@@ -1,6 +1,5 @@
1
1
  """Home of the `IsCompletedObserver` class."""
2
2
 
3
- from typing import Optional, Union, List
4
3
  import numpy as np
5
4
 
6
5
  from job_shop_lib import ScheduledOperation
@@ -40,7 +39,7 @@ class IsCompletedObserver(FeatureObserver):
40
39
  self,
41
40
  dispatcher: Dispatcher,
42
41
  *,
43
- feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
42
+ feature_types: list[FeatureType] | FeatureType | None = None,
44
43
  subscribe: bool = True,
45
44
  ):
46
45
  feature_types = self._get_feature_types_list(feature_types)
@@ -86,8 +85,7 @@ class IsCompletedObserver(FeatureObserver):
86
85
  for op in self.dispatcher.completed_operations():
87
86
  num_completed_ops_per_job[op.operation.job_id] += 1
88
87
  self.features[FeatureType.JOBS][:, 0] = (
89
- num_completed_ops_per_job
90
- == self._num_of_operations_per_job
88
+ num_completed_ops_per_job == self._num_of_operations_per_job
91
89
  ).astype(np.float32)
92
90
 
93
91
  def reset(self):
@@ -1,7 +1,5 @@
1
1
  """Home of the `IsReadyObserver` class."""
2
2
 
3
- from typing import List
4
-
5
3
  from job_shop_lib.dispatching.feature_observers import (
6
4
  FeatureObserver,
7
5
  FeatureType,
@@ -18,7 +16,7 @@ class IsReadyObserver(FeatureObserver):
18
16
  feature_ids = self._get_ready_feature_ids(feature_type)
19
17
  feature[feature_ids, 0] = 1.0
20
18
 
21
- def _get_ready_feature_ids(self, feature_type: FeatureType) -> List[int]:
19
+ def _get_ready_feature_ids(self, feature_type: FeatureType) -> list[int]:
22
20
  if feature_type == FeatureType.OPERATIONS:
23
21
  return self._get_ready_operations()
24
22
  if feature_type == FeatureType.MACHINES:
@@ -30,6 +28,6 @@ class IsReadyObserver(FeatureObserver):
30
28
  def reset(self):
31
29
  self.initialize_features()
32
30
 
33
- def _get_ready_operations(self) -> List[int]:
31
+ def _get_ready_operations(self) -> list[int]:
34
32
  available_operations = self.dispatcher.available_operations()
35
33
  return [operation.operation_id for operation in available_operations]
@@ -5,8 +5,6 @@ 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 Dict, Union
9
-
10
8
  from enum import Enum
11
9
  from collections.abc import Callable
12
10
 
@@ -33,7 +31,7 @@ class DispatchingRuleType(str, Enum):
33
31
 
34
32
 
35
33
  def dispatching_rule_factory(
36
- dispatching_rule: Union[str, DispatchingRuleType],
34
+ dispatching_rule: str | DispatchingRuleType,
37
35
  ) -> Callable[[Dispatcher], Operation]:
38
36
  """Creates and returns a dispatching rule function based on the specified
39
37
  dispatching rule name.
@@ -57,7 +55,7 @@ def dispatching_rule_factory(
57
55
  If the dispatching_rule argument is not recognized or it is
58
56
  not supported.
59
57
  """
60
- dispatching_rules: Dict[
58
+ dispatching_rules: dict[
61
59
  DispatchingRuleType,
62
60
  Callable[[Dispatcher], Operation],
63
61
  ] = {
@@ -1,6 +1,5 @@
1
1
  """Home of the `DispatchingRuleSolver` class."""
2
2
 
3
- from typing import Optional, Union
4
3
  from collections.abc import Callable, Iterable
5
4
 
6
5
  from job_shop_lib import JobShopInstance, Schedule, Operation, BaseSolver
@@ -66,24 +65,19 @@ class DispatchingRuleSolver(BaseSolver):
66
65
 
67
66
  def __init__(
68
67
  self,
69
- dispatching_rule: Union[
70
- str, Callable[[Dispatcher], Operation]
71
- ] = DispatchingRuleType.MOST_WORK_REMAINING,
72
- machine_chooser: Union[
73
- str, Callable[[Dispatcher, Operation], int]
74
- ] = MachineChooserType.FIRST,
75
- ready_operations_filter: Optional[
76
- Union[
77
- Iterable[
78
- Union[
79
- ReadyOperationsFilter, str, ReadyOperationsFilterType
80
- ]
81
- ],
82
- str,
83
- ReadyOperationsFilterType,
84
- ReadyOperationsFilter,
85
- ]
86
- ] = (
68
+ dispatching_rule: (
69
+ str | Callable[[Dispatcher], Operation]
70
+ ) = DispatchingRuleType.MOST_WORK_REMAINING,
71
+ machine_chooser: (
72
+ str | Callable[[Dispatcher, Operation], int]
73
+ ) = MachineChooserType.FIRST,
74
+ ready_operations_filter: (
75
+ Iterable[ReadyOperationsFilter | str | ReadyOperationsFilterType]
76
+ | str
77
+ | ReadyOperationsFilterType
78
+ | ReadyOperationsFilter
79
+ | None
80
+ ) = (
87
81
  ReadyOperationsFilterType.DOMINATED_OPERATIONS,
88
82
  ReadyOperationsFilterType.NON_IMMEDIATE_OPERATIONS,
89
83
  ),
@@ -108,7 +102,7 @@ class DispatchingRuleSolver(BaseSolver):
108
102
  def solve(
109
103
  self,
110
104
  instance: JobShopInstance,
111
- dispatcher: Optional[Dispatcher] = None,
105
+ dispatcher: Dispatcher | None = None,
112
106
  ) -> Schedule:
113
107
  """Solves the instance using the dispatching rule and machine chooser
114
108
  algorithms.
@@ -159,6 +153,7 @@ class DispatchingRuleSolver(BaseSolver):
159
153
  if __name__ == "__main__":
160
154
  import time
161
155
  import cProfile
156
+
162
157
  # import pstats
163
158
  # from io import StringIO
164
159
  from job_shop_lib.benchmarking import (
@@ -6,7 +6,6 @@ which operations are selected for execution based on certain criteria such as
6
6
  shortest processing time, first come first served, etc.
7
7
  """
8
8
 
9
- from typing import List, Optional
10
9
  from collections.abc import Callable, Sequence
11
10
  import random
12
11
 
@@ -65,7 +64,7 @@ def random_operation_rule(dispatcher: Dispatcher) -> Operation:
65
64
 
66
65
 
67
66
  def score_based_rule(
68
- score_function: Callable[[Dispatcher], Sequence[float]]
67
+ score_function: Callable[[Dispatcher], Sequence[float]],
69
68
  ) -> Callable[[Dispatcher], Operation]:
70
69
  """Creates a dispatching rule based on a scoring function.
71
70
 
@@ -89,7 +88,7 @@ def score_based_rule(
89
88
 
90
89
 
91
90
  def score_based_rule_with_tie_breaker(
92
- score_functions: List[Callable[[Dispatcher], Sequence[int]]],
91
+ score_functions: list[Callable[[Dispatcher], Sequence[int]]],
93
92
  ) -> Callable[[Dispatcher], Operation]:
94
93
  """Creates a dispatching rule based on multiple scoring functions.
95
94
 
@@ -123,7 +122,7 @@ def score_based_rule_with_tie_breaker(
123
122
  # -----------------
124
123
 
125
124
 
126
- def shortest_processing_time_score(dispatcher: Dispatcher) -> List[int]:
125
+ def shortest_processing_time_score(dispatcher: Dispatcher) -> list[int]:
127
126
  """Scores each job based on the duration of the next operation."""
128
127
  num_jobs = dispatcher.instance.num_jobs
129
128
  scores = [0] * num_jobs
@@ -132,7 +131,7 @@ def shortest_processing_time_score(dispatcher: Dispatcher) -> List[int]:
132
131
  return scores
133
132
 
134
133
 
135
- def first_come_first_served_score(dispatcher: Dispatcher) -> List[int]:
134
+ def first_come_first_served_score(dispatcher: Dispatcher) -> list[int]:
136
135
  """Scores each job based on the position of the next operation."""
137
136
  num_jobs = dispatcher.instance.num_jobs
138
137
  scores = [0] * num_jobs
@@ -154,9 +153,9 @@ class MostWorkRemainingScorer: # pylint: disable=too-few-public-methods
154
153
  """
155
154
 
156
155
  def __init__(self) -> None:
157
- self._duration_observer: Optional[DurationObserver] = None
158
- self._is_ready_observer: Optional[IsReadyObserver] = None
159
- self._current_dispatcher: Optional[Dispatcher] = None
156
+ self._duration_observer: DurationObserver | None = None
157
+ self._is_ready_observer: IsReadyObserver | None = None
158
+ self._current_dispatcher: Dispatcher | None = None
160
159
 
161
160
  def __call__(self, dispatcher: Dispatcher) -> Sequence[int]:
162
161
  """Scores each job based on the remaining work in the job."""
@@ -198,7 +197,7 @@ observer_based_most_work_remaining_rule = score_based_rule(
198
197
  )
199
198
 
200
199
 
201
- def most_operations_remaining_score(dispatcher: Dispatcher) -> List[int]:
200
+ def most_operations_remaining_score(dispatcher: Dispatcher) -> list[int]:
202
201
  """Scores each job based on the remaining operations in the job."""
203
202
  num_jobs = dispatcher.instance.num_jobs
204
203
  scores = [0] * num_jobs
@@ -207,7 +206,7 @@ def most_operations_remaining_score(dispatcher: Dispatcher) -> List[int]:
207
206
  return scores
208
207
 
209
208
 
210
- def random_score(dispatcher: Dispatcher) -> List[int]:
209
+ def random_score(dispatcher: Dispatcher) -> list[int]:
211
210
  """Scores each job randomly."""
212
211
  return [
213
212
  random.randint(0, 100) for _ in range(dispatcher.instance.num_jobs)
@@ -5,7 +5,6 @@ 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 Union, Dict
9
8
  from enum import Enum
10
9
  from collections.abc import Callable
11
10
  import random
@@ -26,7 +25,7 @@ MachineChooser = Callable[[Dispatcher, Operation], int]
26
25
 
27
26
 
28
27
  def machine_chooser_factory(
29
- machine_chooser: Union[str, MachineChooser],
28
+ machine_chooser: str | MachineChooser,
30
29
  ) -> MachineChooser:
31
30
  """Creates and returns a machine chooser function based on the specified
32
31
  machine chooser strategy name.
@@ -51,7 +50,7 @@ def machine_chooser_factory(
51
50
  If the ``machine_chooser`` argument is not recognized or
52
51
  is not supported.
53
52
  """
54
- machine_choosers: Dict[str, Callable[[Dispatcher, Operation], int]] = {
53
+ machine_choosers: dict[str, Callable[[Dispatcher, Operation], int]] = {
55
54
  MachineChooserType.FIRST: lambda _, operation: operation.machines[0],
56
55
  MachineChooserType.RANDOM: lambda _, operation: random.choice(
57
56
  operation.machines
@@ -1,6 +1,5 @@
1
1
  """Utility functions."""
2
2
 
3
- from typing import Union, List
4
3
  import time
5
4
  from collections.abc import Callable
6
5
  import pandas as pd
@@ -11,12 +10,12 @@ from job_shop_lib.dispatching import Dispatcher
11
10
 
12
11
 
13
12
  def benchmark_dispatching_rules(
14
- dispatching_rules: Union[
15
- List[Union[str, Callable[[Dispatcher], Operation]]],
16
- List[str],
17
- List[Callable[[Dispatcher], Operation]]
18
- ],
19
- instances: List[JobShopInstance],
13
+ dispatching_rules: (
14
+ list[str | Callable[[Dispatcher], Operation]]
15
+ | list[str]
16
+ | list[Callable[[Dispatcher], Operation]]
17
+ ),
18
+ instances: list[JobShopInstance],
20
19
  ) -> pd.DataFrame:
21
20
  """Benchmark multiple dispatching rules on multiple JobShopInstances.
22
21
 
@@ -112,7 +111,7 @@ if __name__ == "__main__":
112
111
  instances_ = [load_benchmark_instance(f"ta{i:02d}") for i in range(1, 3)]
113
112
 
114
113
  # Define rules
115
- rules_: List[str | Callable[[Dispatcher], Operation]] = [
114
+ rules_: list[str | Callable[[Dispatcher], Operation]] = [
116
115
  "most_work_remaining",
117
116
  "shortest_processing_time",
118
117
  most_work_remaining_rule,