job-shop-lib 1.0.0b5__py3-none-any.whl → 1.0.2__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.
- job_shop_lib/__init__.py +1 -1
- job_shop_lib/_job_shop_instance.py +2 -2
- job_shop_lib/_operation.py +9 -3
- job_shop_lib/_scheduled_operation.py +3 -0
- job_shop_lib/benchmarking/__init__.py +1 -0
- job_shop_lib/dispatching/__init__.py +12 -10
- job_shop_lib/dispatching/_dispatcher.py +6 -13
- job_shop_lib/dispatching/_factories.py +3 -3
- job_shop_lib/dispatching/_optimal_operations_observer.py +0 -2
- job_shop_lib/dispatching/_ready_operation_filters.py +4 -4
- job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +11 -6
- job_shop_lib/dispatching/feature_observers/_factory.py +8 -3
- job_shop_lib/dispatching/feature_observers/_feature_observer.py +1 -1
- job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +35 -67
- job_shop_lib/dispatching/rules/__init__.py +11 -8
- job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +1 -1
- job_shop_lib/dispatching/rules/_machine_chooser_factory.py +3 -2
- job_shop_lib/generation/__init__.py +12 -1
- job_shop_lib/graphs/__init__.py +42 -8
- job_shop_lib/graphs/_build_resource_task_graphs.py +1 -1
- job_shop_lib/graphs/_job_shop_graph.py +38 -19
- job_shop_lib/graphs/graph_updaters/__init__.py +5 -1
- job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py +108 -0
- job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +3 -1
- job_shop_lib/graphs/graph_updaters/_utils.py +2 -2
- job_shop_lib/py.typed +0 -0
- job_shop_lib/reinforcement_learning/__init__.py +13 -7
- job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +1 -1
- job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +102 -24
- job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +11 -2
- job_shop_lib/reinforcement_learning/_types_and_constants.py +11 -10
- job_shop_lib/reinforcement_learning/_utils.py +29 -0
- job_shop_lib/visualization/gantt/__init__.py +7 -3
- job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py +5 -2
- job_shop_lib/visualization/graphs/__init__.py +1 -0
- job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +53 -19
- {job_shop_lib-1.0.0b5.dist-info → job_shop_lib-1.0.2.dist-info}/METADATA +19 -18
- {job_shop_lib-1.0.0b5.dist-info → job_shop_lib-1.0.2.dist-info}/RECORD +40 -38
- {job_shop_lib-1.0.0b5.dist-info → job_shop_lib-1.0.2.dist-info}/LICENSE +0 -0
- {job_shop_lib-1.0.0b5.dist-info → job_shop_lib-1.0.2.dist-info}/WHEEL +0 -0
job_shop_lib/__init__.py
CHANGED
@@ -16,8 +16,8 @@ class JobShopInstance:
|
|
16
16
|
"""Data structure to store a Job Shop Scheduling Problem instance.
|
17
17
|
|
18
18
|
Additional attributes such as ``num_machines`` or ``durations_matrix`` can
|
19
|
-
be computed from the instance and are cached for performance
|
20
|
-
require expensive computations.
|
19
|
+
be computed from the instance and are cached for performance since they
|
20
|
+
might require expensive computations.
|
21
21
|
|
22
22
|
Methods:
|
23
23
|
|
job_shop_lib/_operation.py
CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
from typing import Union, List
|
6
6
|
|
7
|
-
from job_shop_lib.exceptions import
|
7
|
+
from job_shop_lib.exceptions import ValidationError
|
8
8
|
|
9
9
|
|
10
10
|
class Operation:
|
@@ -81,8 +81,14 @@ class Operation:
|
|
81
81
|
If the operation has multiple machines in its list.
|
82
82
|
"""
|
83
83
|
if len(self.machines) > 1:
|
84
|
-
raise
|
85
|
-
"Operation has multiple machines."
|
84
|
+
raise ValidationError(
|
85
|
+
"Operation has multiple machines. The `machine_id` property "
|
86
|
+
"should only be used when working with a classic JSSP "
|
87
|
+
"instance. This error prevents silent bugs. To handle "
|
88
|
+
"operations with more machines you have to use the machines "
|
89
|
+
"attribute. If you get this error using `job_shop_lib` "
|
90
|
+
"objects, it means that that object does not support "
|
91
|
+
"operations with multiple machines yet."
|
86
92
|
)
|
87
93
|
return self.machines[0]
|
88
94
|
|
@@ -9,13 +9,15 @@ Problem step-by-step.
|
|
9
9
|
HistoryObserver
|
10
10
|
UnscheduledOperationsObserver
|
11
11
|
OptimalOperationsObserver
|
12
|
-
ReadyOperationsFilter
|
13
12
|
DispatcherObserverConfig
|
13
|
+
ReadyOperationsFilter
|
14
|
+
ReadyOperationsFilterType
|
15
|
+
ready_operations_filter_factory
|
14
16
|
filter_dominated_operations
|
15
17
|
filter_non_immediate_machines
|
18
|
+
filter_non_idle_machines
|
19
|
+
filter_non_immediate_operations
|
16
20
|
create_composite_operation_filter
|
17
|
-
ReadyOperationsFilterType
|
18
|
-
ready_operations_filter_factory
|
19
21
|
|
20
22
|
Dispatching refers to the decision-making process of selecting which job
|
21
23
|
should be processed next on a particular machine when that machine becomes
|
@@ -45,17 +47,17 @@ from ._factories import (
|
|
45
47
|
|
46
48
|
__all__ = [
|
47
49
|
"Dispatcher",
|
48
|
-
"filter_dominated_operations",
|
49
|
-
"filter_non_immediate_machines",
|
50
|
-
"create_composite_operation_filter",
|
51
|
-
"ReadyOperationsFilterType",
|
52
|
-
"ready_operations_filter_factory",
|
53
50
|
"DispatcherObserver",
|
54
51
|
"HistoryObserver",
|
55
|
-
"DispatcherObserverConfig",
|
56
52
|
"UnscheduledOperationsObserver",
|
53
|
+
"OptimalOperationsObserver",
|
54
|
+
"DispatcherObserverConfig",
|
57
55
|
"ReadyOperationsFilter",
|
56
|
+
"ReadyOperationsFilterType",
|
57
|
+
"ready_operations_filter_factory",
|
58
|
+
"filter_dominated_operations",
|
59
|
+
"filter_non_immediate_machines",
|
58
60
|
"filter_non_idle_machines",
|
59
61
|
"filter_non_immediate_operations",
|
60
|
-
"
|
62
|
+
"create_composite_operation_filter",
|
61
63
|
]
|
@@ -336,8 +336,7 @@ class Dispatcher:
|
|
336
336
|
The operation to be scheduled.
|
337
337
|
machine_id:
|
338
338
|
The id of the machine on which the operation is to be
|
339
|
-
scheduled.
|
340
|
-
next available time for the operation on any machine.
|
339
|
+
scheduled.
|
341
340
|
"""
|
342
341
|
return max(
|
343
342
|
self._machine_next_available_time[machine_id],
|
@@ -459,12 +458,11 @@ class Dispatcher:
|
|
459
458
|
return unscheduled_operations
|
460
459
|
|
461
460
|
@_dispatcher_cache
|
462
|
-
def scheduled_operations(self) -> List[
|
461
|
+
def scheduled_operations(self) -> List[ScheduledOperation]:
|
463
462
|
"""Returns the list of operations that have been scheduled."""
|
464
463
|
scheduled_operations = []
|
465
|
-
for
|
466
|
-
|
467
|
-
scheduled_operations.extend(operations)
|
464
|
+
for machine_schedule in self.schedule.schedule:
|
465
|
+
scheduled_operations.extend(machine_schedule)
|
468
466
|
return scheduled_operations
|
469
467
|
|
470
468
|
@_dispatcher_cache
|
@@ -532,19 +530,14 @@ class Dispatcher:
|
|
532
530
|
return scheduled_operation.end_time - adjusted_start_time
|
533
531
|
|
534
532
|
@_dispatcher_cache
|
535
|
-
def completed_operations(self) -> Set[
|
533
|
+
def completed_operations(self) -> Set[ScheduledOperation]:
|
536
534
|
"""Returns the set of operations that have been completed.
|
537
535
|
|
538
536
|
This method returns the operations that have been scheduled and the
|
539
537
|
current time is greater than or equal to the end time of the operation.
|
540
538
|
"""
|
541
539
|
scheduled_operations = set(self.scheduled_operations())
|
542
|
-
ongoing_operations = set(
|
543
|
-
map(
|
544
|
-
lambda scheduled_op: scheduled_op.operation,
|
545
|
-
self.ongoing_operations(),
|
546
|
-
)
|
547
|
-
)
|
540
|
+
ongoing_operations = set(self.ongoing_operations())
|
548
541
|
completed_operations = scheduled_operations - ongoing_operations
|
549
542
|
return completed_operations
|
550
543
|
|
@@ -95,9 +95,9 @@ def ready_operations_filter_factory(
|
|
95
95
|
|
96
96
|
Args:
|
97
97
|
filter_name:
|
98
|
-
The name of the filter function to be used.
|
99
|
-
|
100
|
-
|
98
|
+
The name of the filter function to be used. See
|
99
|
+
:class:`ReadyOperationsFilterType` for supported values.
|
100
|
+
Alternatively, a custom filter function can be passed directly.
|
101
101
|
|
102
102
|
Returns:
|
103
103
|
A function that takes a :class:`~job_shop_lib.dispatching.Dispatcher`
|
@@ -20,8 +20,6 @@ class OptimalOperationsObserver(DispatcherObserver):
|
|
20
20
|
based on the reference schedule.
|
21
21
|
reference_schedule: The reference schedule used to determine optimal
|
22
22
|
operations.
|
23
|
-
_operation_to_scheduled: Dictionary mapping operations to their
|
24
|
-
scheduled versions in the reference schedule.
|
25
23
|
|
26
24
|
Args:
|
27
25
|
dispatcher: The dispatcher instance to observe.
|
@@ -153,16 +153,16 @@ def _get_min_machine_end_times(
|
|
153
153
|
|
154
154
|
|
155
155
|
def _get_immediate_machines(
|
156
|
-
|
156
|
+
dispatcher: Dispatcher, available_operations: List[Operation]
|
157
157
|
) -> List[bool]:
|
158
158
|
"""Returns the machine ids of the machines that have at least one
|
159
159
|
operation with the lowest start time (i.e. the start time)."""
|
160
|
-
working_machines = [False] *
|
160
|
+
working_machines = [False] * dispatcher.instance.num_machines
|
161
161
|
# We can't use the current_time directly because it will cause
|
162
162
|
# an infinite loop.
|
163
|
-
current_time =
|
163
|
+
current_time = dispatcher.min_start_time(available_operations)
|
164
164
|
for op in available_operations:
|
165
165
|
for machine_id in op.machines:
|
166
|
-
if
|
166
|
+
if dispatcher.start_time(op, machine_id) == current_time:
|
167
167
|
working_machines[machine_id] = True
|
168
168
|
return working_machines
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
from collections import defaultdict
|
4
4
|
from collections.abc import Sequence
|
5
|
-
from typing import List, Dict, Union, Optional
|
5
|
+
from typing import List, Dict, Union, Optional, Type
|
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.
|
8
|
+
# python >=3.10
|
9
9
|
from typing_extensions import Self
|
10
10
|
import numpy as np
|
11
11
|
from numpy.typing import NDArray
|
@@ -18,6 +18,7 @@ from job_shop_lib.dispatching.feature_observers import (
|
|
18
18
|
FeatureType,
|
19
19
|
FeatureObserverConfig,
|
20
20
|
feature_observer_factory,
|
21
|
+
FeatureObserverType,
|
21
22
|
)
|
22
23
|
|
23
24
|
|
@@ -104,7 +105,14 @@ class CompositeFeatureObserver(FeatureObserver):
|
|
104
105
|
def from_feature_observer_configs(
|
105
106
|
cls,
|
106
107
|
dispatcher: Dispatcher,
|
107
|
-
feature_observer_configs: Sequence[
|
108
|
+
feature_observer_configs: Sequence[
|
109
|
+
Union[
|
110
|
+
str,
|
111
|
+
FeatureObserverType,
|
112
|
+
Type[FeatureObserver],
|
113
|
+
FeatureObserverConfig,
|
114
|
+
],
|
115
|
+
],
|
108
116
|
subscribe: bool = True,
|
109
117
|
) -> Self:
|
110
118
|
"""Creates the composite feature observer.
|
@@ -178,9 +186,6 @@ if __name__ == "__main__":
|
|
178
186
|
import time
|
179
187
|
from job_shop_lib.benchmarking import load_benchmark_instance
|
180
188
|
from job_shop_lib.dispatching.rules import DispatchingRuleSolver
|
181
|
-
from job_shop_lib.dispatching.feature_observers import (
|
182
|
-
FeatureObserverType,
|
183
|
-
)
|
184
189
|
|
185
190
|
ta80 = load_benchmark_instance("ta80")
|
186
191
|
|
@@ -39,9 +39,14 @@ class FeatureObserverType(str, Enum):
|
|
39
39
|
# FeatureObserverConfig = DispatcherObserverConfig[
|
40
40
|
# Type[FeatureObserver] | FeatureObserverType | str
|
41
41
|
# ]
|
42
|
-
FeatureObserverConfig = DispatcherObserverConfig[
|
43
|
-
|
44
|
-
]
|
42
|
+
# FeatureObserverConfig = DispatcherObserverConfig[
|
43
|
+
# Union[Type[FeatureObserver], FeatureObserverType, str]
|
44
|
+
# ]
|
45
|
+
FeatureObserverConfig = (
|
46
|
+
DispatcherObserverConfig[Type[FeatureObserver]]
|
47
|
+
| DispatcherObserverConfig[FeatureObserverType]
|
48
|
+
| DispatcherObserverConfig[str]
|
49
|
+
)
|
45
50
|
|
46
51
|
|
47
52
|
def feature_observer_factory(
|
@@ -36,7 +36,7 @@ class FeatureObserver(DispatcherObserver):
|
|
36
36
|
individually. Furthermore, machine learning models can be trained on these
|
37
37
|
arrays to predict the best dispatching decisions.
|
38
38
|
|
39
|
-
Arrays use the data type ``np.float32``.
|
39
|
+
Arrays use the data type ``np.float32``.
|
40
40
|
|
41
41
|
New :class:`FeatureObservers` must inherit from this class, and re-define
|
42
42
|
the class attributes ``_singleton`` (defualt ), ``_feature_size``
|
@@ -4,14 +4,10 @@ from typing import Optional, Union, List
|
|
4
4
|
import numpy as np
|
5
5
|
|
6
6
|
from job_shop_lib import ScheduledOperation
|
7
|
-
from job_shop_lib.dispatching import
|
8
|
-
Dispatcher,
|
9
|
-
DispatcherObserver,
|
10
|
-
)
|
7
|
+
from job_shop_lib.dispatching import Dispatcher
|
11
8
|
from job_shop_lib.dispatching.feature_observers import (
|
12
9
|
FeatureObserver,
|
13
10
|
FeatureType,
|
14
|
-
RemainingOperationsObserver,
|
15
11
|
)
|
16
12
|
|
17
13
|
|
@@ -40,15 +36,6 @@ class IsCompletedObserver(FeatureObserver):
|
|
40
36
|
or manually updated.
|
41
37
|
"""
|
42
38
|
|
43
|
-
__slots__ = {
|
44
|
-
"remaining_ops_per_machine": (
|
45
|
-
"The number of unscheduled operations per machine."
|
46
|
-
),
|
47
|
-
"remaining_ops_per_job": (
|
48
|
-
"The number of unscheduled operations per job."
|
49
|
-
),
|
50
|
-
}
|
51
|
-
|
52
39
|
def __init__(
|
53
40
|
self,
|
54
41
|
dispatcher: Dispatcher,
|
@@ -57,11 +44,16 @@ class IsCompletedObserver(FeatureObserver):
|
|
57
44
|
subscribe: bool = True,
|
58
45
|
):
|
59
46
|
feature_types = self._get_feature_types_list(feature_types)
|
60
|
-
self.
|
61
|
-
|
47
|
+
self._num_of_operations_per_machine = np.array(
|
48
|
+
[
|
49
|
+
len(operations_by_machine)
|
50
|
+
for operations_by_machine in (
|
51
|
+
dispatcher.instance.operations_by_machine
|
52
|
+
)
|
53
|
+
]
|
62
54
|
)
|
63
|
-
self.
|
64
|
-
(dispatcher.instance.
|
55
|
+
self._num_of_operations_per_job = np.array(
|
56
|
+
[len(job) for job in dispatcher.instance.jobs]
|
65
57
|
)
|
66
58
|
super().__init__(
|
67
59
|
dispatcher,
|
@@ -70,60 +62,36 @@ class IsCompletedObserver(FeatureObserver):
|
|
70
62
|
)
|
71
63
|
|
72
64
|
def initialize_features(self):
|
73
|
-
def _has_same_features(observer: DispatcherObserver) -> bool:
|
74
|
-
if not isinstance(observer, RemainingOperationsObserver):
|
75
|
-
return False
|
76
|
-
return all(
|
77
|
-
feature_type in observer.features
|
78
|
-
for feature_type in remaining_ops_feature_types
|
79
|
-
)
|
80
|
-
|
81
|
-
self.set_features_to_zero()
|
82
|
-
|
83
|
-
remaining_ops_feature_types = [
|
84
|
-
feature_type
|
85
|
-
for feature_type in self.features.keys()
|
86
|
-
if feature_type != FeatureType.OPERATIONS
|
87
|
-
]
|
88
|
-
remaining_ops_observer = self.dispatcher.create_or_get_observer(
|
89
|
-
RemainingOperationsObserver,
|
90
|
-
condition=_has_same_features,
|
91
|
-
feature_types=remaining_ops_feature_types,
|
92
|
-
)
|
93
|
-
if FeatureType.JOBS in self.features:
|
94
|
-
self.remaining_ops_per_job = remaining_ops_observer.features[
|
95
|
-
FeatureType.JOBS
|
96
|
-
].copy()
|
97
|
-
if FeatureType.MACHINES in self.features:
|
98
|
-
self.remaining_ops_per_machine = remaining_ops_observer.features[
|
99
|
-
FeatureType.MACHINES
|
100
|
-
].copy()
|
101
|
-
|
102
|
-
def reset(self):
|
103
|
-
self.initialize_features()
|
104
|
-
|
105
|
-
def update(self, scheduled_operation: ScheduledOperation):
|
106
65
|
if FeatureType.OPERATIONS in self.features:
|
107
66
|
completed_operations = [
|
108
|
-
op.operation_id
|
67
|
+
op.operation.operation_id
|
109
68
|
for op in self.dispatcher.completed_operations()
|
110
69
|
]
|
111
70
|
self.features[FeatureType.OPERATIONS][completed_operations, 0] = 1
|
112
71
|
if FeatureType.MACHINES in self.features:
|
113
|
-
|
114
|
-
|
115
|
-
] -= 1
|
116
|
-
is_completed = (
|
117
|
-
self.remaining_ops_per_machine[
|
118
|
-
scheduled_operation.operation.machines, 0
|
119
|
-
]
|
120
|
-
== 0
|
72
|
+
num_completed_ops_per_machine = np.zeros(
|
73
|
+
len(self._num_of_operations_per_machine)
|
121
74
|
)
|
122
|
-
self.
|
123
|
-
|
124
|
-
|
75
|
+
for op in self.dispatcher.completed_operations():
|
76
|
+
for machine_id in op.operation.machines:
|
77
|
+
num_completed_ops_per_machine[machine_id] += 1
|
78
|
+
self.features[FeatureType.MACHINES][:, 0] = (
|
79
|
+
num_completed_ops_per_machine
|
80
|
+
== self._num_of_operations_per_machine
|
81
|
+
).astype(np.float32)
|
125
82
|
if FeatureType.JOBS in self.features:
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
self.
|
83
|
+
num_completed_ops_per_job = np.zeros(
|
84
|
+
len(self._num_of_operations_per_job)
|
85
|
+
)
|
86
|
+
for op in self.dispatcher.completed_operations():
|
87
|
+
num_completed_ops_per_job[op.operation.job_id] += 1
|
88
|
+
self.features[FeatureType.JOBS][:, 0] = (
|
89
|
+
num_completed_ops_per_job
|
90
|
+
== self._num_of_operations_per_job
|
91
|
+
).astype(np.float32)
|
92
|
+
|
93
|
+
def reset(self):
|
94
|
+
self.set_features_to_zero()
|
95
|
+
|
96
|
+
def update(self, scheduled_operation: ScheduledOperation):
|
97
|
+
self.initialize_features()
|
@@ -3,17 +3,19 @@
|
|
3
3
|
Main objects:
|
4
4
|
|
5
5
|
.. autosummary::
|
6
|
+
:nosignatures:
|
6
7
|
|
7
8
|
DispatchingRuleSolver
|
8
|
-
dispatching_rule_factory
|
9
9
|
DispatchingRuleType
|
10
|
-
MachineChooserType
|
11
10
|
dispatching_rule_factory
|
11
|
+
MachineChooserType
|
12
12
|
machine_chooser_factory
|
13
|
+
MachineChooser
|
13
14
|
|
14
15
|
Dispatching rules:
|
15
16
|
|
16
17
|
.. autosummary::
|
18
|
+
:nosignatures:
|
17
19
|
|
18
20
|
shortest_processing_time_rule
|
19
21
|
first_come_first_served_rule
|
@@ -27,6 +29,7 @@ Dispatching rules:
|
|
27
29
|
Dispatching rule scorers:
|
28
30
|
|
29
31
|
.. autosummary::
|
32
|
+
:nosignatures:
|
30
33
|
|
31
34
|
shortest_processing_time_score
|
32
35
|
first_come_first_served_score
|
@@ -65,6 +68,11 @@ from ._dispatching_rule_solver import DispatchingRuleSolver
|
|
65
68
|
|
66
69
|
|
67
70
|
__all__ = [
|
71
|
+
"DispatchingRuleSolver",
|
72
|
+
"DispatchingRuleType",
|
73
|
+
"dispatching_rule_factory",
|
74
|
+
"MachineChooserType",
|
75
|
+
"machine_chooser_factory",
|
68
76
|
"shortest_processing_time_rule",
|
69
77
|
"first_come_first_served_rule",
|
70
78
|
"most_work_remaining_rule",
|
@@ -72,16 +80,11 @@ __all__ = [
|
|
72
80
|
"random_operation_rule",
|
73
81
|
"score_based_rule",
|
74
82
|
"score_based_rule_with_tie_breaker",
|
83
|
+
"observer_based_most_work_remaining_rule",
|
75
84
|
"shortest_processing_time_score",
|
76
85
|
"first_come_first_served_score",
|
77
86
|
"MostWorkRemainingScorer",
|
78
87
|
"most_operations_remaining_score",
|
79
88
|
"random_score",
|
80
|
-
"dispatching_rule_factory",
|
81
|
-
"DispatchingRuleType",
|
82
|
-
"MachineChooserType",
|
83
|
-
"machine_chooser_factory",
|
84
89
|
"MachineChooser",
|
85
|
-
"DispatchingRuleSolver",
|
86
|
-
"observer_based_most_work_remaining_rule",
|
87
90
|
]
|
@@ -33,7 +33,7 @@ class DispatchingRuleType(str, Enum):
|
|
33
33
|
|
34
34
|
|
35
35
|
def dispatching_rule_factory(
|
36
|
-
dispatching_rule: Union[str, DispatchingRuleType,
|
36
|
+
dispatching_rule: Union[str, DispatchingRuleType],
|
37
37
|
) -> Callable[[Dispatcher], Operation]:
|
38
38
|
"""Creates and returns a dispatching rule function based on the specified
|
39
39
|
dispatching rule name.
|
@@ -47,8 +47,9 @@ def machine_chooser_factory(
|
|
47
47
|
machine chooser strategy.
|
48
48
|
|
49
49
|
Raises:
|
50
|
-
|
51
|
-
not
|
50
|
+
ValidationError:
|
51
|
+
If the ``machine_chooser`` argument is not recognized or
|
52
|
+
is not supported.
|
52
53
|
"""
|
53
54
|
machine_choosers: Dict[str, Callable[[Dispatcher, Operation], int]] = {
|
54
55
|
MachineChooserType.FIRST: lambda _, operation: operation.machines[0],
|
@@ -1,4 +1,15 @@
|
|
1
|
-
"""Package for generating job shop instances.
|
1
|
+
"""Package for generating job shop instances.
|
2
|
+
|
3
|
+
.. autosummary::
|
4
|
+
:nosignatures:
|
5
|
+
|
6
|
+
InstanceGenerator
|
7
|
+
GeneralInstanceGenerator
|
8
|
+
generate_duration_matrix
|
9
|
+
generate_machine_matrix_with_recirculation
|
10
|
+
generate_machine_matrix_without_recirculation
|
11
|
+
|
12
|
+
"""
|
2
13
|
|
3
14
|
from job_shop_lib.generation._utils import (
|
4
15
|
generate_duration_matrix,
|
job_shop_lib/graphs/__init__.py
CHANGED
@@ -2,15 +2,47 @@
|
|
2
2
|
|
3
3
|
The main classes and functions available in this package are:
|
4
4
|
|
5
|
+
Main objects:
|
6
|
+
|
5
7
|
.. autosummary::
|
8
|
+
:nosignatures:
|
9
|
+
|
6
10
|
JobShopGraph
|
7
11
|
Node
|
8
12
|
NodeType
|
13
|
+
EdgeType
|
14
|
+
NODE_ATTR
|
15
|
+
|
16
|
+
Build functions:
|
17
|
+
|
18
|
+
.. autosummary::
|
19
|
+
:nosignatures:
|
20
|
+
|
9
21
|
build_disjunctive_graph
|
10
|
-
build_solved_disjunctive_graph
|
11
22
|
build_resource_task_graph
|
12
23
|
build_complete_resource_task_graph
|
13
24
|
build_resource_task_graph_with_jobs
|
25
|
+
build_solved_disjunctive_graph
|
26
|
+
|
27
|
+
Add functions:
|
28
|
+
|
29
|
+
.. autosummary::
|
30
|
+
:nosignatures:
|
31
|
+
|
32
|
+
add_disjunctive_edges
|
33
|
+
add_conjunctive_edges
|
34
|
+
add_source_sink_nodes
|
35
|
+
add_source_sink_edges
|
36
|
+
add_same_job_operations_edges
|
37
|
+
add_machine_nodes
|
38
|
+
add_operation_machine_edges
|
39
|
+
add_machine_machine_edges
|
40
|
+
add_job_nodes
|
41
|
+
add_operation_job_edges
|
42
|
+
add_global_node
|
43
|
+
add_machine_global_edges
|
44
|
+
add_job_global_edges
|
45
|
+
add_job_job_edges
|
14
46
|
|
15
47
|
"""
|
16
48
|
|
@@ -38,23 +70,25 @@ from job_shop_lib.graphs._build_resource_task_graphs import (
|
|
38
70
|
add_global_node,
|
39
71
|
add_machine_global_edges,
|
40
72
|
add_job_global_edges,
|
73
|
+
add_job_job_edges,
|
41
74
|
)
|
42
75
|
|
43
76
|
|
44
77
|
__all__ = [
|
45
|
-
"EdgeType",
|
46
|
-
"NodeType",
|
47
|
-
"Node",
|
48
78
|
"JobShopGraph",
|
79
|
+
"Node",
|
80
|
+
"NodeType",
|
81
|
+
"EdgeType",
|
49
82
|
"NODE_ATTR",
|
50
83
|
"build_disjunctive_graph",
|
84
|
+
"build_resource_task_graph",
|
85
|
+
"build_complete_resource_task_graph",
|
86
|
+
"build_resource_task_graph_with_jobs",
|
87
|
+
"build_solved_disjunctive_graph",
|
51
88
|
"add_disjunctive_edges",
|
52
89
|
"add_conjunctive_edges",
|
53
90
|
"add_source_sink_nodes",
|
54
91
|
"add_source_sink_edges",
|
55
|
-
"build_resource_task_graph",
|
56
|
-
"build_complete_resource_task_graph",
|
57
|
-
"build_resource_task_graph_with_jobs",
|
58
92
|
"add_same_job_operations_edges",
|
59
93
|
"add_machine_nodes",
|
60
94
|
"add_operation_machine_edges",
|
@@ -64,5 +98,5 @@ __all__ = [
|
|
64
98
|
"add_global_node",
|
65
99
|
"add_machine_global_edges",
|
66
100
|
"add_job_global_edges",
|
67
|
-
"
|
101
|
+
"add_job_job_edges",
|
68
102
|
]
|
@@ -1,7 +1,7 @@
|
|
1
1
|
"""Contains helper functions to build the resource-task graphs from a job shop
|
2
2
|
instance.
|
3
3
|
|
4
|
-
The
|
4
|
+
The resource-task graph (originally called agent-task graph) was introduced by
|
5
5
|
Junyoung Park et al. (2021).
|
6
6
|
In contrast to the disjunctive graph, instead of connecting operations that
|
7
7
|
share the same resources directly by disjunctive edges, operation nodes are
|