job-shop-lib 1.0.0a5__py3-none-any.whl → 1.0.0b1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. job_shop_lib/__init__.py +1 -1
  2. job_shop_lib/_job_shop_instance.py +34 -29
  3. job_shop_lib/_operation.py +4 -2
  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 +4 -4
  9. job_shop_lib/dispatching/_factories.py +4 -2
  10. job_shop_lib/dispatching/_history_observer.py +2 -1
  11. job_shop_lib/dispatching/_ready_operation_filters.py +19 -18
  12. job_shop_lib/dispatching/_unscheduled_operations_observer.py +4 -3
  13. job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +7 -8
  14. job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +3 -1
  15. job_shop_lib/dispatching/feature_observers/_factory.py +13 -14
  16. job_shop_lib/dispatching/feature_observers/_feature_observer.py +9 -8
  17. job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +2 -1
  18. job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +4 -2
  19. job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +4 -2
  20. job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +23 -15
  21. job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -8
  22. job_shop_lib/dispatching/rules/_machine_chooser_factory.py +4 -3
  23. job_shop_lib/dispatching/rules/_utils.py +9 -8
  24. job_shop_lib/generation/__init__.py +8 -0
  25. job_shop_lib/generation/_general_instance_generator.py +42 -64
  26. job_shop_lib/generation/_instance_generator.py +11 -7
  27. job_shop_lib/generation/_transformations.py +5 -4
  28. job_shop_lib/generation/_utils.py +124 -0
  29. job_shop_lib/graphs/__init__.py +7 -7
  30. job_shop_lib/graphs/{_build_agent_task_graph.py → _build_resource_task_graphs.py} +26 -24
  31. job_shop_lib/graphs/_job_shop_graph.py +17 -13
  32. job_shop_lib/graphs/_node.py +6 -4
  33. job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +4 -2
  34. job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +40 -20
  35. job_shop_lib/reinforcement_learning/_reward_observers.py +3 -1
  36. job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +89 -22
  37. job_shop_lib/reinforcement_learning/_types_and_constants.py +1 -1
  38. job_shop_lib/reinforcement_learning/_utils.py +3 -3
  39. job_shop_lib/visualization/__init__.py +0 -60
  40. job_shop_lib/visualization/gantt/__init__.py +48 -0
  41. job_shop_lib/visualization/{_gantt_chart_creator.py → gantt/_gantt_chart_creator.py} +12 -12
  42. job_shop_lib/visualization/{_gantt_chart_video_and_gif_creation.py → gantt/_gantt_chart_video_and_gif_creation.py} +22 -22
  43. job_shop_lib/visualization/{_plot_gantt_chart.py → gantt/_plot_gantt_chart.py} +12 -13
  44. job_shop_lib/visualization/graphs/__init__.py +29 -0
  45. job_shop_lib/visualization/{_plot_disjunctive_graph.py → graphs/_plot_disjunctive_graph.py} +18 -16
  46. job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +389 -0
  47. {job_shop_lib-1.0.0a5.dist-info → job_shop_lib-1.0.0b1.dist-info}/METADATA +17 -15
  48. job_shop_lib-1.0.0b1.dist-info/RECORD +69 -0
  49. job_shop_lib/visualization/_plot_agent_task_graph.py +0 -276
  50. job_shop_lib-1.0.0a5.dist-info/RECORD +0 -66
  51. {job_shop_lib-1.0.0a5.dist-info → job_shop_lib-1.0.0b1.dist-info}/LICENSE +0 -0
  52. {job_shop_lib-1.0.0a5.dist-info → job_shop_lib-1.0.0b1.dist-info}/WHEEL +0 -0
@@ -4,6 +4,7 @@ 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
7
8
  from collections.abc import Callable
8
9
 
9
10
  from job_shop_lib import Operation
@@ -11,13 +12,13 @@ from job_shop_lib.dispatching import Dispatcher
11
12
 
12
13
 
13
14
  ReadyOperationsFilter = Callable[
14
- [Dispatcher, list[Operation]], list[Operation]
15
+ [Dispatcher, List[Operation]], List[Operation]
15
16
  ]
16
17
 
17
18
 
18
19
  def filter_non_idle_machines(
19
- dispatcher: Dispatcher, operations: list[Operation]
20
- ) -> list[Operation]:
20
+ dispatcher: Dispatcher, operations: List[Operation]
21
+ ) -> List[Operation]:
21
22
  """Filters out all the operations associated with non-idle machines.
22
23
 
23
24
  A machine is considered idle if there are no ongoing operations
@@ -40,7 +41,7 @@ def filter_non_idle_machines(
40
41
 
41
42
  # Filter operations to keep those that are associated with at least one
42
43
  # idle machine
43
- filtered_operations: list[Operation] = []
44
+ filtered_operations: List[Operation] = []
44
45
  for operation in operations:
45
46
  if all(
46
47
  machine_id in non_idle_machines
@@ -54,7 +55,7 @@ def filter_non_idle_machines(
54
55
 
55
56
  def _get_non_idle_machines(
56
57
  dispatcher: Dispatcher, current_time: int
57
- ) -> set[int]:
58
+ ) -> Set[int]:
58
59
  """Returns the set of machine ids that are currently busy (i.e., have at
59
60
  least one uncompleted operation)."""
60
61
 
@@ -70,8 +71,8 @@ def _get_non_idle_machines(
70
71
 
71
72
 
72
73
  def filter_non_immediate_operations(
73
- dispatcher: Dispatcher, operations: list[Operation]
74
- ) -> list[Operation]:
74
+ dispatcher: Dispatcher, operations: List[Operation]
75
+ ) -> List[Operation]:
75
76
  """Filters out all the operations that can't start immediately.
76
77
 
77
78
  An operation can start immediately if its earliest start time is the
@@ -86,7 +87,7 @@ def filter_non_immediate_operations(
86
87
  """
87
88
 
88
89
  min_start_time = dispatcher.min_start_time(operations)
89
- immediate_operations: list[Operation] = []
90
+ immediate_operations: List[Operation] = []
90
91
  for operation in operations:
91
92
  start_time = dispatcher.earliest_start_time(operation)
92
93
  if start_time == min_start_time:
@@ -96,8 +97,8 @@ def filter_non_immediate_operations(
96
97
 
97
98
 
98
99
  def filter_dominated_operations(
99
- dispatcher: Dispatcher, operations: list[Operation]
100
- ) -> list[Operation]:
100
+ dispatcher: Dispatcher, operations: List[Operation]
101
+ ) -> List[Operation]:
101
102
  """Filters out all the operations that are dominated.
102
103
  An operation is dominated if there is another operation that ends before
103
104
  it starts on the same machine.
@@ -105,7 +106,7 @@ def filter_dominated_operations(
105
106
 
106
107
  min_machine_end_times = _get_min_machine_end_times(dispatcher, operations)
107
108
 
108
- non_dominated_operations: list[Operation] = []
109
+ non_dominated_operations: List[Operation] = []
109
110
  for operation in operations:
110
111
  # One benchmark instance has an operation with duration 0
111
112
  if operation.duration == 0:
@@ -121,13 +122,13 @@ def filter_dominated_operations(
121
122
 
122
123
 
123
124
  def filter_non_immediate_machines(
124
- dispatcher: Dispatcher, operations: list[Operation]
125
- ) -> list[Operation]:
125
+ dispatcher: Dispatcher, operations: List[Operation]
126
+ ) -> List[Operation]:
126
127
  """Filters out all the operations associated with machines which earliest
127
128
  operation is not the current time."""
128
129
 
129
130
  is_immediate_machine = _get_immediate_machines(dispatcher, operations)
130
- non_dominated_operations: list[Operation] = []
131
+ non_dominated_operations: List[Operation] = []
131
132
  for operation in operations:
132
133
  if any(
133
134
  is_immediate_machine[machine_id]
@@ -139,8 +140,8 @@ def filter_non_immediate_machines(
139
140
 
140
141
 
141
142
  def _get_min_machine_end_times(
142
- dispatcher: Dispatcher, available_operations: list[Operation]
143
- ) -> list[int | float]:
143
+ dispatcher: Dispatcher, available_operations: List[Operation]
144
+ ) -> List[float]:
144
145
  end_times_per_machine = [float("inf")] * dispatcher.instance.num_machines
145
146
  for op in available_operations:
146
147
  for machine_id in op.machines:
@@ -152,8 +153,8 @@ def _get_min_machine_end_times(
152
153
 
153
154
 
154
155
  def _get_immediate_machines(
155
- self: Dispatcher, available_operations: list[Operation]
156
- ) -> list[bool]:
156
+ self: Dispatcher, available_operations: List[Operation]
157
+ ) -> List[bool]:
157
158
  """Returns the machine ids of the machines that have at least one
158
159
  operation with the lowest start time (i.e. the start time)."""
159
160
  working_machines = [False] * self.instance.num_machines
@@ -3,7 +3,7 @@
3
3
  import collections
4
4
  from collections.abc import Iterable
5
5
  import itertools
6
- from typing import Deque
6
+ from typing import Deque, List
7
7
 
8
8
  from job_shop_lib import Operation, ScheduledOperation
9
9
  from job_shop_lib.dispatching import DispatcherObserver, Dispatcher
@@ -19,7 +19,7 @@ class UnscheduledOperationsObserver(DispatcherObserver):
19
19
 
20
20
  def __init__(self, dispatcher: Dispatcher, *, subscribe: bool = True):
21
21
  super().__init__(dispatcher, subscribe=subscribe)
22
- self.unscheduled_operations_per_job: list[Deque[Operation]] = []
22
+ self.unscheduled_operations_per_job: List[Deque[Operation]] = []
23
23
  self.reset()
24
24
  # In case the dispatcher has already scheduled some operations,
25
25
  # we need to remove them.
@@ -51,7 +51,8 @@ class UnscheduledOperationsObserver(DispatcherObserver):
51
51
  unscheduled operations.
52
52
 
53
53
  Args:
54
- scheduled_operation: The operation that has been scheduled.
54
+ scheduled_operation:
55
+ The operation that has been scheduled.
55
56
  """
56
57
  job_id = scheduled_operation.operation.job_id
57
58
  job_deque = self.unscheduled_operations_per_job[job_id]
@@ -2,12 +2,11 @@
2
2
 
3
3
  from collections import defaultdict
4
4
  from collections.abc import Sequence
5
-
5
+ from typing import List, Dict, Union, Optional
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
- # python 3.10.
8
+ # python >=3.8
9
9
  from typing_extensions import Self
10
-
11
10
  import numpy as np
12
11
  from numpy.typing import NDArray
13
12
  import pandas as pd
@@ -77,8 +76,8 @@ class CompositeFeatureObserver(FeatureObserver):
77
76
  dispatcher: Dispatcher,
78
77
  *,
79
78
  subscribe: bool = True,
80
- feature_types: list[FeatureType] | FeatureType | None = None,
81
- feature_observers: list[FeatureObserver] | None = None,
79
+ feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
80
+ feature_observers: Optional[List[FeatureObserver]] = None,
82
81
  ):
83
82
  if feature_observers is None:
84
83
  feature_observers = [
@@ -97,7 +96,7 @@ class CompositeFeatureObserver(FeatureObserver):
97
96
  f"Composite feature types: {feature_types}"
98
97
  )
99
98
  self.feature_observers = feature_observers
100
- self.column_names: dict[FeatureType, list[str]] = defaultdict(list)
99
+ self.column_names: Dict[FeatureType, List[str]] = defaultdict(list)
101
100
  super().__init__(dispatcher, subscribe=subscribe)
102
101
  self._set_column_names()
103
102
 
@@ -129,7 +128,7 @@ class CompositeFeatureObserver(FeatureObserver):
129
128
  return composite_observer
130
129
 
131
130
  @property
132
- def features_as_dataframe(self) -> dict[FeatureType, pd.DataFrame]:
131
+ def features_as_dataframe(self) -> Dict[FeatureType, pd.DataFrame]:
133
132
  """Returns the features as a dictionary of `pd.DataFrame` instances."""
134
133
  return {
135
134
  feature_type: pd.DataFrame(
@@ -139,7 +138,7 @@ class CompositeFeatureObserver(FeatureObserver):
139
138
  }
140
139
 
141
140
  def initialize_features(self):
142
- features: dict[FeatureType, list[NDArray[np.float32]]] = defaultdict(
141
+ features: Dict[FeatureType, List[NDArray[np.float32]]] = defaultdict(
143
142
  list
144
143
  )
145
144
  for observer in self.feature_observers:
@@ -1,5 +1,7 @@
1
1
  """Home of the `EarliestStartTimeObserver` class."""
2
2
 
3
+ from typing import List, Optional, Union
4
+
3
5
  import numpy as np
4
6
  from numpy.typing import NDArray
5
7
 
@@ -75,7 +77,7 @@ class EarliestStartTimeObserver(FeatureObserver):
75
77
  dispatcher: Dispatcher,
76
78
  *,
77
79
  subscribe: bool = True,
78
- feature_types: list[FeatureType] | FeatureType | None = None,
80
+ feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
79
81
  ):
80
82
 
81
83
  # Earliest start times initialization
@@ -1,6 +1,7 @@
1
1
  """Contains factory functions for creating :class:`FeatureObserver`s."""
2
2
 
3
3
  from enum import Enum
4
+ from typing import Union, Type
4
5
 
5
6
  from job_shop_lib.dispatching import DispatcherObserverConfig
6
7
  from job_shop_lib.dispatching.feature_observers import (
@@ -36,22 +37,20 @@ class FeatureObserverType(str, Enum):
36
37
 
37
38
 
38
39
  # FeatureObserverConfig = DispatcherObserverConfig[
39
- # type[FeatureObserver] | FeatureObserverType | str
40
+ # Type[FeatureObserver] | FeatureObserverType | str
40
41
  # ]
41
- FeatureObserverConfig = (
42
- DispatcherObserverConfig[type[FeatureObserver]]
43
- | DispatcherObserverConfig[FeatureObserverType]
44
- | DispatcherObserverConfig[str]
45
- )
42
+ FeatureObserverConfig = DispatcherObserverConfig[
43
+ Union[Type[FeatureObserver], FeatureObserverType, str]
44
+ ]
46
45
 
47
46
 
48
47
  def feature_observer_factory(
49
- feature_observer_type: (
50
- str
51
- | FeatureObserverType
52
- | type[FeatureObserver]
53
- | FeatureObserverConfig
54
- ),
48
+ feature_observer_type: Union[
49
+ str,
50
+ FeatureObserverType,
51
+ Type[FeatureObserver],
52
+ FeatureObserverConfig
53
+ ],
55
54
  **kwargs,
56
55
  ) -> FeatureObserver:
57
56
  """Creates and returns a :class:`FeatureObserver` based on the specified
@@ -73,12 +72,12 @@ def feature_observer_factory(
73
72
  **feature_observer_type.kwargs,
74
73
  **kwargs,
75
74
  )
76
- # if the instance is of type type[FeatureObserver] we can just
75
+ # if the instance is of type Type[FeatureObserver] we can just
77
76
  # call the object constructor with the keyword arguments
78
77
  if isinstance(feature_observer_type, type):
79
78
  return feature_observer_type(**kwargs)
80
79
 
81
- mapping: dict[FeatureObserverType, type[FeatureObserver]] = {
80
+ mapping: dict[FeatureObserverType, Type[FeatureObserver]] = {
82
81
  FeatureObserverType.IS_READY: IsReadyObserver,
83
82
  FeatureObserverType.EARLIEST_START_TIME: EarliestStartTimeObserver,
84
83
  FeatureObserverType.DURATION: DurationObserver,
@@ -1,6 +1,7 @@
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
4
5
 
5
6
  import numpy as np
6
7
 
@@ -64,7 +65,7 @@ class FeatureObserver(DispatcherObserver):
64
65
  """
65
66
 
66
67
  _is_singleton = False
67
- _feature_sizes: dict[FeatureType, int] | int = 1
68
+ _feature_sizes: Union[Dict[FeatureType, int], int] = 1
68
69
  _supported_feature_types = list(FeatureType)
69
70
 
70
71
  __slots__ = {
@@ -84,7 +85,7 @@ class FeatureObserver(DispatcherObserver):
84
85
  dispatcher: Dispatcher,
85
86
  *,
86
87
  subscribe: bool = True,
87
- feature_types: list[FeatureType] | FeatureType | None = None,
88
+ feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
88
89
  ):
89
90
  feature_types = self._get_feature_types_list(feature_types)
90
91
  if isinstance(self._feature_sizes, int):
@@ -116,7 +117,7 @@ class FeatureObserver(DispatcherObserver):
116
117
  self.initialize_features()
117
118
 
118
119
  @property
119
- def feature_sizes(self) -> dict[FeatureType, int]:
120
+ def feature_sizes(self) -> Dict[FeatureType, int]:
120
121
  """Returns the size of the features.
121
122
 
122
123
  The size of the features is the number of values being observed for
@@ -134,12 +135,12 @@ class FeatureObserver(DispatcherObserver):
134
135
  return self._feature_sizes
135
136
 
136
137
  @property
137
- def supported_feature_types(self) -> list[FeatureType]:
138
+ def supported_feature_types(self) -> List[FeatureType]:
138
139
  """Returns the supported feature types."""
139
140
  return self._supported_feature_types
140
141
 
141
142
  @property
142
- def feature_dimensions(self) -> dict[FeatureType, tuple[int, int]]:
143
+ def feature_dimensions(self) -> Dict[FeatureType, Tuple[int, int]]:
143
144
  """A dictionary containing the shape of each :class:`FeatureType`."""
144
145
  feature_dimensions = {}
145
146
  for feature_type, array in self.features.items():
@@ -170,7 +171,7 @@ class FeatureObserver(DispatcherObserver):
170
171
  self.initialize_features()
171
172
 
172
173
  def set_features_to_zero(
173
- self, exclude: FeatureType | list[FeatureType] | None = None
174
+ self, exclude: Optional[Union[FeatureType, List[FeatureType]]] = None
174
175
  ):
175
176
  """Sets all features to zero except for the ones specified in
176
177
  ``exclude``.
@@ -196,8 +197,8 @@ class FeatureObserver(DispatcherObserver):
196
197
 
197
198
  def _get_feature_types_list(
198
199
  self,
199
- feature_types: list[FeatureType] | FeatureType | None,
200
- ) -> list[FeatureType]:
200
+ feature_types: Optional[Union[List[FeatureType], FeatureType]],
201
+ ) -> List[FeatureType]:
201
202
  """Returns a list of feature types.
202
203
 
203
204
  Args:
@@ -1,5 +1,6 @@
1
1
  """Home of the `IsCompletedObserver` class."""
2
2
 
3
+ from typing import Optional, Union, List
3
4
  import numpy as np
4
5
 
5
6
  from job_shop_lib import ScheduledOperation
@@ -52,7 +53,7 @@ class IsCompletedObserver(FeatureObserver):
52
53
  self,
53
54
  dispatcher: Dispatcher,
54
55
  *,
55
- feature_types: list[FeatureType] | FeatureType | None = None,
56
+ feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
56
57
  subscribe: bool = True,
57
58
  ):
58
59
  feature_types = self._get_feature_types_list(feature_types)
@@ -1,5 +1,7 @@
1
1
  """Home of the `IsReadyObserver` class."""
2
2
 
3
+ from typing import List
4
+
3
5
  from job_shop_lib.dispatching.feature_observers import (
4
6
  FeatureObserver,
5
7
  FeatureType,
@@ -16,7 +18,7 @@ class IsReadyObserver(FeatureObserver):
16
18
  feature_ids = self._get_ready_feature_ids(feature_type)
17
19
  feature[feature_ids, 0] = 1.0
18
20
 
19
- def _get_ready_feature_ids(self, feature_type: FeatureType) -> list[int]:
21
+ def _get_ready_feature_ids(self, feature_type: FeatureType) -> List[int]:
20
22
  if feature_type == FeatureType.OPERATIONS:
21
23
  return self._get_ready_operations()
22
24
  if feature_type == FeatureType.MACHINES:
@@ -28,6 +30,6 @@ class IsReadyObserver(FeatureObserver):
28
30
  def reset(self):
29
31
  self.initialize_features()
30
32
 
31
- def _get_ready_operations(self) -> list[int]:
33
+ def _get_ready_operations(self) -> List[int]:
32
34
  available_operations = self.dispatcher.available_operations()
33
35
  return [operation.operation_id for operation in available_operations]
@@ -5,6 +5,8 @@ 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
+
8
10
  from enum import Enum
9
11
  from collections.abc import Callable
10
12
 
@@ -31,7 +33,7 @@ class DispatchingRuleType(str, Enum):
31
33
 
32
34
 
33
35
  def dispatching_rule_factory(
34
- dispatching_rule: str | DispatchingRuleType,
36
+ dispatching_rule: Union[str, DispatchingRuleType,]
35
37
  ) -> Callable[[Dispatcher], Operation]:
36
38
  """Creates and returns a dispatching rule function based on the specified
37
39
  dispatching rule name.
@@ -55,7 +57,7 @@ def dispatching_rule_factory(
55
57
  If the dispatching_rule argument is not recognized or it is
56
58
  not supported.
57
59
  """
58
- dispatching_rules: dict[
60
+ dispatching_rules: Dict[
59
61
  DispatchingRuleType,
60
62
  Callable[[Dispatcher], Operation],
61
63
  ] = {
@@ -1,5 +1,6 @@
1
1
  """Home of the `DispatchingRuleSolver` class."""
2
2
 
3
+ from typing import Optional, Union
3
4
  from collections.abc import Callable, Iterable
4
5
 
5
6
  from job_shop_lib import JobShopInstance, Schedule, Operation, BaseSolver
@@ -65,21 +66,26 @@ class DispatchingRuleSolver(BaseSolver):
65
66
 
66
67
  def __init__(
67
68
  self,
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
- ) = (
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
+ ] = (
81
87
  ReadyOperationsFilterType.DOMINATED_OPERATIONS,
82
- ReadyOperationsFilterType.NON_IDLE_MACHINES,
88
+ ReadyOperationsFilterType.NON_IMMEDIATE_OPERATIONS,
83
89
  ),
84
90
  ):
85
91
  if isinstance(dispatching_rule, str):
@@ -100,7 +106,9 @@ class DispatchingRuleSolver(BaseSolver):
100
106
  self.ready_operations_filter = ready_operations_filter
101
107
 
102
108
  def solve(
103
- self, instance: JobShopInstance, dispatcher: Dispatcher | None = None
109
+ self,
110
+ instance: JobShopInstance,
111
+ dispatcher: Optional[Dispatcher] = None,
104
112
  ) -> Schedule:
105
113
  """Returns a schedule for the given job shop instance using the
106
114
  dispatching rule algorithm."""
@@ -6,6 +6,7 @@ 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
9
10
  from collections.abc import Callable, Sequence
10
11
  import random
11
12
 
@@ -88,7 +89,7 @@ def score_based_rule(
88
89
 
89
90
 
90
91
  def score_based_rule_with_tie_breaker(
91
- score_functions: list[Callable[[Dispatcher], Sequence[int]]],
92
+ score_functions: List[Callable[[Dispatcher], Sequence[int]]],
92
93
  ) -> Callable[[Dispatcher], Operation]:
93
94
  """Creates a dispatching rule based on multiple scoring functions.
94
95
 
@@ -122,7 +123,7 @@ def score_based_rule_with_tie_breaker(
122
123
  # -----------------
123
124
 
124
125
 
125
- def shortest_processing_time_score(dispatcher: Dispatcher) -> list[int]:
126
+ def shortest_processing_time_score(dispatcher: Dispatcher) -> List[int]:
126
127
  """Scores each job based on the duration of the next operation."""
127
128
  num_jobs = dispatcher.instance.num_jobs
128
129
  scores = [0] * num_jobs
@@ -131,7 +132,7 @@ def shortest_processing_time_score(dispatcher: Dispatcher) -> list[int]:
131
132
  return scores
132
133
 
133
134
 
134
- def first_come_first_served_score(dispatcher: Dispatcher) -> list[int]:
135
+ def first_come_first_served_score(dispatcher: Dispatcher) -> List[int]:
135
136
  """Scores each job based on the position of the next operation."""
136
137
  num_jobs = dispatcher.instance.num_jobs
137
138
  scores = [0] * num_jobs
@@ -153,9 +154,9 @@ class MostWorkRemainingScorer: # pylint: disable=too-few-public-methods
153
154
  """
154
155
 
155
156
  def __init__(self) -> None:
156
- self._duration_observer: DurationObserver | None = None
157
- self._is_ready_observer: IsReadyObserver | None = None
158
- self._current_dispatcher: Dispatcher | None = None
157
+ self._duration_observer: Optional[DurationObserver] = None
158
+ self._is_ready_observer: Optional[IsReadyObserver] = None
159
+ self._current_dispatcher: Optional[Dispatcher] = None
159
160
 
160
161
  def __call__(self, dispatcher: Dispatcher) -> Sequence[int]:
161
162
  """Scores each job based on the remaining work in the job."""
@@ -197,7 +198,7 @@ observer_based_most_work_remaining_rule = score_based_rule(
197
198
  )
198
199
 
199
200
 
200
- def most_operations_remaining_score(dispatcher: Dispatcher) -> list[int]:
201
+ def most_operations_remaining_score(dispatcher: Dispatcher) -> List[int]:
201
202
  """Scores each job based on the remaining operations in the job."""
202
203
  num_jobs = dispatcher.instance.num_jobs
203
204
  scores = [0] * num_jobs
@@ -206,7 +207,7 @@ def most_operations_remaining_score(dispatcher: Dispatcher) -> list[int]:
206
207
  return scores
207
208
 
208
209
 
209
- def random_score(dispatcher: Dispatcher) -> list[int]:
210
+ def random_score(dispatcher: Dispatcher) -> List[int]:
210
211
  """Scores each job randomly."""
211
212
  return [
212
213
  random.randint(0, 100) for _ in range(dispatcher.instance.num_jobs)
@@ -5,6 +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 Union, Dict
8
9
  from enum import Enum
9
10
  from collections.abc import Callable
10
11
  import random
@@ -25,7 +26,7 @@ MachineChooser = Callable[[Dispatcher, Operation], int]
25
26
 
26
27
 
27
28
  def machine_chooser_factory(
28
- machine_chooser: str | MachineChooser,
29
+ machine_chooser: Union[str, MachineChooser],
29
30
  ) -> MachineChooser:
30
31
  """Creates and returns a machine chooser function based on the specified
31
32
  machine chooser strategy name.
@@ -36,7 +37,7 @@ def machine_chooser_factory(
36
37
  machine randomly.
37
38
 
38
39
  Args:
39
- machine_chooser (str): The name of the machine chooser strategy to be
40
+ machine_chooser: The name of the machine chooser strategy to be
40
41
  used. Supported values are 'first' and 'random'.
41
42
 
42
43
  Returns:
@@ -49,7 +50,7 @@ def machine_chooser_factory(
49
50
  ValueError: If the machine_chooser argument is not recognized or is
50
51
  not supported.
51
52
  """
52
- machine_choosers: dict[str, Callable[[Dispatcher, Operation], int]] = {
53
+ machine_choosers: Dict[str, Callable[[Dispatcher, Operation], int]] = {
53
54
  MachineChooserType.FIRST: lambda _, operation: operation.machines[0],
54
55
  MachineChooserType.RANDOM: lambda _, operation: random.choice(
55
56
  operation.machines
@@ -1,5 +1,6 @@
1
1
  """Utility functions."""
2
2
 
3
+ from typing import Union, List
3
4
  import time
4
5
  from collections.abc import Callable
5
6
  import pandas as pd
@@ -10,12 +11,12 @@ from job_shop_lib.dispatching import Dispatcher
10
11
 
11
12
 
12
13
  def benchmark_dispatching_rules(
13
- dispatching_rules: (
14
- list[str | Callable[[Dispatcher], Operation]]
15
- | list[str]
16
- | list[Callable[[Dispatcher], Operation]]
17
- ),
18
- instances: list[JobShopInstance],
14
+ dispatching_rules: Union[
15
+ List[Union[str, Callable[[Dispatcher], Operation]]],
16
+ List[str],
17
+ List[Callable[[Dispatcher], Operation]]
18
+ ],
19
+ instances: List[JobShopInstance],
19
20
  ) -> pd.DataFrame:
20
21
  """Benchmark multiple dispatching rules on multiple JobShopInstances.
21
22
 
@@ -30,7 +31,7 @@ def benchmark_dispatching_rules(
30
31
  either a string (name of a built-in rule) or a callable
31
32
  (custom rule function).
32
33
  instances:
33
- iList of :class:`JobShopInstance` objects to be solved.
34
+ List of :class:`JobShopInstance` objects to be solved.
34
35
 
35
36
  Returns:
36
37
  A pandas DataFrame with columns:
@@ -111,7 +112,7 @@ if __name__ == "__main__":
111
112
  instances_ = [load_benchmark_instance(f"ta{i:02d}") for i in range(1, 3)]
112
113
 
113
114
  # Define rules
114
- rules_: list[str | Callable[[Dispatcher], Operation]] = [
115
+ rules_: List[str | Callable[[Dispatcher], Operation]] = [
115
116
  "most_work_remaining",
116
117
  "shortest_processing_time",
117
118
  most_work_remaining_rule,
@@ -1,5 +1,10 @@
1
1
  """Package for generating job shop instances."""
2
2
 
3
+ from job_shop_lib.generation._utils import (
4
+ generate_duration_matrix,
5
+ generate_machine_matrix_with_recirculation,
6
+ generate_machine_matrix_without_recirculation,
7
+ )
3
8
  from job_shop_lib.generation._instance_generator import InstanceGenerator
4
9
  from job_shop_lib.generation._general_instance_generator import (
5
10
  GeneralInstanceGenerator,
@@ -8,4 +13,7 @@ from job_shop_lib.generation._general_instance_generator import (
8
13
  __all__ = [
9
14
  "InstanceGenerator",
10
15
  "GeneralInstanceGenerator",
16
+ "generate_duration_matrix",
17
+ "generate_machine_matrix_with_recirculation",
18
+ "generate_machine_matrix_without_recirculation",
11
19
  ]