job-shop-lib 1.0.0a2__tar.gz → 1.0.0a4__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/PKG-INFO +15 -3
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/README.md +14 -2
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_job_shop_instance.py +119 -55
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_operation.py +18 -7
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_schedule.py +13 -15
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_scheduled_operation.py +17 -18
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/__init__.py +4 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_dispatcher.py +36 -47
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_dispatcher_observer_config.py +15 -2
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_factories.py +10 -2
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_ready_operation_filters.py +80 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +0 -1
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_factory.py +21 -18
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +1 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +1 -1
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +44 -25
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -9
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/_general_instance_generator.py +33 -34
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/_instance_generator.py +14 -17
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/_transformations.py +11 -8
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/__init__.py +3 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_build_disjunctive_graph.py +41 -3
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +11 -13
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +17 -20
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/__init__.py +16 -7
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +69 -57
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +43 -32
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_types_and_constants.py +2 -2
- job_shop_lib-1.0.0a4/job_shop_lib/visualization/__init__.py +60 -0
- job_shop_lib-1.0.0a4/job_shop_lib/visualization/_gantt_chart_creator.py +257 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/visualization/_gantt_chart_video_and_gif_creation.py +68 -37
- job_shop_lib-1.0.0a4/job_shop_lib/visualization/_plot_disjunctive_graph.py +382 -0
- job_shop_lib-1.0.0a2/job_shop_lib/visualization/_gantt_chart.py → job_shop_lib-1.0.0a4/job_shop_lib/visualization/_plot_gantt_chart.py +78 -14
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/pyproject.toml +2 -1
- job_shop_lib-1.0.0a2/job_shop_lib/visualization/__init__.py +0 -41
- job_shop_lib-1.0.0a2/job_shop_lib/visualization/_disjunctive_graph.py +0 -210
- job_shop_lib-1.0.0a2/job_shop_lib/visualization/_gantt_chart_creator.py +0 -219
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/LICENSE +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_base_solver.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/benchmarking/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/benchmarking/_load_benchmark.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/constraint_programming/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/constraint_programming/_ortools_solver.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_history_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_utils.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/exceptions.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_build_agent_task_graph.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_constants.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_job_shop_graph.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_node.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_reward_observers.py +0 -0
- {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_utils.py +0 -0
- /job_shop_lib-1.0.0a2/job_shop_lib/visualization/_agent_task_graph.py → /job_shop_lib-1.0.0a4/job_shop_lib/visualization/_plot_agent_task_graph.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: job-shop-lib
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.0a4
|
4
4
|
Summary: An easy-to-use and modular Python library for the Job Shop Scheduling Problem (JSSP)
|
5
5
|
License: MIT
|
6
6
|
Author: Pabloo22
|
@@ -29,6 +29,7 @@ Description-Content-Type: text/markdown
|
|
29
29
|
<h1>JobShopLib</h1>
|
30
30
|
|
31
31
|
[](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml)
|
32
|
+
[](https://job-shop-lib.readthedocs.io/en/latest/?badge=latest)
|
32
33
|

|
33
34
|
[](https://github.com/psf/black)
|
34
35
|
[](https://opensource.org/licenses/MIT)
|
@@ -39,7 +40,7 @@ JobShopLib is a Python package for creating, solving, and visualizing Job Shop S
|
|
39
40
|
|
40
41
|
It follows a modular design, allowing users to easily extend the library with new solvers, dispatching rules, visualization functions, etc.
|
41
42
|
|
42
|
-
See [
|
43
|
+
See the [documentation](https://job-shop-lib.readthedocs.io/en/latest/) for more details about the latest version.
|
43
44
|
|
44
45
|
## Installation :package:
|
45
46
|
|
@@ -47,12 +48,23 @@ See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcoww
|
|
47
48
|
|
48
49
|
JobShopLib is distributed on [PyPI](https://pypi.org/project/job-shop-lib/) and it supports Python 3.10+.
|
49
50
|
|
50
|
-
You can install the latest version using `pip`:
|
51
|
+
You can install the latest stable version (version 0.5.1) using `pip`:
|
51
52
|
|
52
53
|
```bash
|
53
54
|
pip install job-shop-lib
|
54
55
|
```
|
55
56
|
|
57
|
+
See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide!
|
58
|
+
|
59
|
+
|
60
|
+
Version 1.0.0 is currently in alpha stage and can be installed with:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
pip install job-shop-lib==1.0.0a4
|
64
|
+
```
|
65
|
+
|
66
|
+
Although this version is not stable and may contain breaking changes in subsequent releases, it is recommended to install it to access the new reinforcement learning environments and familiarize yourself with new changes (see the [latest pull requests](https://github.com/Pabloo22/job_shop_lib/pulls?q=is%3Apr+is%3Aclosed)). This version is the first one with a [documentation page](https://job-shop-lib.readthedocs.io/en/latest/).
|
67
|
+
|
56
68
|
<!-- end installation -->
|
57
69
|
|
58
70
|
<!-- key features -->
|
@@ -5,6 +5,7 @@
|
|
5
5
|
<h1>JobShopLib</h1>
|
6
6
|
|
7
7
|
[](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml)
|
8
|
+
[](https://job-shop-lib.readthedocs.io/en/latest/?badge=latest)
|
8
9
|

|
9
10
|
[](https://github.com/psf/black)
|
10
11
|
[](https://opensource.org/licenses/MIT)
|
@@ -15,7 +16,7 @@ JobShopLib is a Python package for creating, solving, and visualizing Job Shop S
|
|
15
16
|
|
16
17
|
It follows a modular design, allowing users to easily extend the library with new solvers, dispatching rules, visualization functions, etc.
|
17
18
|
|
18
|
-
See [
|
19
|
+
See the [documentation](https://job-shop-lib.readthedocs.io/en/latest/) for more details about the latest version.
|
19
20
|
|
20
21
|
## Installation :package:
|
21
22
|
|
@@ -23,12 +24,23 @@ See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcoww
|
|
23
24
|
|
24
25
|
JobShopLib is distributed on [PyPI](https://pypi.org/project/job-shop-lib/) and it supports Python 3.10+.
|
25
26
|
|
26
|
-
You can install the latest version using `pip`:
|
27
|
+
You can install the latest stable version (version 0.5.1) using `pip`:
|
27
28
|
|
28
29
|
```bash
|
29
30
|
pip install job-shop-lib
|
30
31
|
```
|
31
32
|
|
33
|
+
See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide!
|
34
|
+
|
35
|
+
|
36
|
+
Version 1.0.0 is currently in alpha stage and can be installed with:
|
37
|
+
|
38
|
+
```bash
|
39
|
+
pip install job-shop-lib==1.0.0a4
|
40
|
+
```
|
41
|
+
|
42
|
+
Although this version is not stable and may contain breaking changes in subsequent releases, it is recommended to install it to access the new reinforcement learning environments and familiarize yourself with new changes (see the [latest pull requests](https://github.com/Pabloo22/job_shop_lib/pulls?q=is%3Apr+is%3Aclosed)). This version is the first one with a [documentation page](https://job-shop-lib.readthedocs.io/en/latest/).
|
43
|
+
|
32
44
|
<!-- end installation -->
|
33
45
|
|
34
46
|
<!-- key features -->
|
@@ -15,49 +15,105 @@ from job_shop_lib import Operation
|
|
15
15
|
class JobShopInstance:
|
16
16
|
"""Data structure to store a Job Shop Scheduling Problem instance.
|
17
17
|
|
18
|
-
Additional attributes such as
|
19
|
-
from the instance and are cached for performance if they
|
20
|
-
computations.
|
18
|
+
Additional attributes such as ``num_machines`` or ``durations_matrix`` can
|
19
|
+
be computed from the instance and are cached for performance if they
|
20
|
+
require expensive computations.
|
21
|
+
|
22
|
+
Methods:
|
23
|
+
|
24
|
+
.. autosummary::
|
25
|
+
:nosignatures:
|
26
|
+
|
27
|
+
from_taillard_file
|
28
|
+
to_dict
|
29
|
+
from_matrices
|
30
|
+
set_operation_attributes
|
31
|
+
|
32
|
+
Properties:
|
33
|
+
|
34
|
+
.. autosummary::
|
35
|
+
:nosignatures:
|
36
|
+
|
37
|
+
num_jobs
|
38
|
+
num_machines
|
39
|
+
num_operations
|
40
|
+
is_flexible
|
41
|
+
durations_matrix
|
42
|
+
machines_matrix
|
43
|
+
durations_matrix_array
|
44
|
+
machines_matrix_array
|
45
|
+
operations_by_machine
|
46
|
+
max_duration
|
47
|
+
max_duration_per_job
|
48
|
+
max_duration_per_machine
|
49
|
+
job_durations
|
50
|
+
machine_loads
|
51
|
+
total_duration
|
21
52
|
|
22
53
|
Attributes:
|
23
|
-
jobs:
|
54
|
+
jobs (list[list[Operation]]):
|
24
55
|
A list of lists of operations. Each list of operations represents
|
25
56
|
a job, and the operations are ordered by their position in the job.
|
26
|
-
The
|
27
|
-
the operations are set when the instance is created.
|
28
|
-
name:
|
57
|
+
The ``job_id``, ``position_in_job``, and `operation_id` attributes
|
58
|
+
of the operations are set when the instance is created.
|
59
|
+
name (str):
|
29
60
|
A string with the name of the instance.
|
30
|
-
metadata:
|
61
|
+
metadata (dict[str, Any]):
|
31
62
|
A dictionary with additional information about the instance.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
jobs:
|
66
|
+
A list of lists of operations. Each list of operations
|
67
|
+
represents a job, and the operations are ordered by their
|
68
|
+
position in the job. The ``job_id``, ``position_in_job``, and
|
69
|
+
``operation_id`` attributes of the operations are set when the
|
70
|
+
instance is created.
|
71
|
+
name:
|
72
|
+
A string with the name of the instance.
|
73
|
+
set_operation_attributes:
|
74
|
+
If True, the ``job_id``, ``position_in_job``, and ``operation_id``
|
75
|
+
attributes of the operations are set when the instance is created.
|
76
|
+
See :meth:`set_operation_attributes` for more information. Defaults
|
77
|
+
to True.
|
78
|
+
**metadata:
|
79
|
+
Additional information about the instance.
|
32
80
|
"""
|
33
81
|
|
34
82
|
def __init__(
|
35
83
|
self,
|
36
84
|
jobs: list[list[Operation]],
|
37
85
|
name: str = "JobShopInstance",
|
86
|
+
set_operation_attributes: bool = True,
|
38
87
|
**metadata: Any,
|
39
88
|
):
|
40
|
-
|
89
|
+
self.jobs: list[list[Operation]] = jobs
|
90
|
+
if set_operation_attributes:
|
91
|
+
self.set_operation_attributes()
|
92
|
+
self.name: str = name
|
93
|
+
self.metadata: dict[str, Any] = metadata
|
94
|
+
|
95
|
+
def set_operation_attributes(self):
|
96
|
+
"""Sets the ``job_id``, ``position_in_job``, and ``operation_id``
|
97
|
+
attributes for each operation in the instance.
|
98
|
+
|
99
|
+
The ``job_id`` attribute is set to the id of the job to which the
|
100
|
+
operation belongs.
|
101
|
+
|
102
|
+
The ``position_in_job`` attribute is set to the
|
103
|
+
position of the operation in the job (starts from 0).
|
104
|
+
|
105
|
+
The ``operation_id`` attribute is set to a unique identifier for the
|
106
|
+
operation (starting from 0).
|
107
|
+
|
108
|
+
The formula to compute the ``operation_id`` in a job shop instance with
|
109
|
+
a fixed number of operations per job is:
|
110
|
+
|
111
|
+
.. code-block:: python
|
112
|
+
|
113
|
+
operation_id = job_id * num_operations_per_job + position_in_job
|
41
114
|
|
42
|
-
Args:
|
43
|
-
jobs:
|
44
|
-
A list of lists of operations. Each list of operations
|
45
|
-
represents a job, and the operations are ordered by their
|
46
|
-
position in the job. The `job_id`, `position_in_job`, and
|
47
|
-
`operation_id` attributes of the operations are set when the
|
48
|
-
instance is created.
|
49
|
-
name:
|
50
|
-
A string with the name of the instance.
|
51
|
-
**metadata:
|
52
|
-
Additional information about the instance.
|
53
115
|
"""
|
54
|
-
self.jobs = jobs
|
55
|
-
self.set_operation_attributes()
|
56
|
-
self.name = name
|
57
|
-
self.metadata = metadata
|
58
116
|
|
59
|
-
def set_operation_attributes(self):
|
60
|
-
"""Sets the job_id and position of each operation."""
|
61
117
|
operation_id = 0
|
62
118
|
for job_id, job in enumerate(self.jobs):
|
63
119
|
for position, operation in enumerate(job):
|
@@ -92,8 +148,8 @@ class JobShopInstance:
|
|
92
148
|
Additional information about the instance.
|
93
149
|
|
94
150
|
Returns:
|
95
|
-
A JobShopInstance object with the operations read from the
|
96
|
-
and the name and metadata provided.
|
151
|
+
A :class:`JobShopInstance` object with the operations read from the
|
152
|
+
file, and the name and metadata provided.
|
97
153
|
"""
|
98
154
|
with open(file_path, "r", encoding=encoding) as file:
|
99
155
|
lines = file.readlines()
|
@@ -130,13 +186,17 @@ class JobShopInstance:
|
|
130
186
|
like Taillard's.
|
131
187
|
|
132
188
|
Returns:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
189
|
+
dict[str, Any]: The returned dictionary has the following
|
190
|
+
structure:
|
191
|
+
|
192
|
+
.. code-block:: python
|
193
|
+
|
194
|
+
{
|
195
|
+
"name": self.name,
|
196
|
+
"duration_matrix": self.durations_matrix,
|
197
|
+
"machines_matrix": self.machines_matrix,
|
198
|
+
"metadata": self.metadata,
|
199
|
+
}
|
140
200
|
"""
|
141
201
|
return {
|
142
202
|
"name": self.name,
|
@@ -153,7 +213,8 @@ class JobShopInstance:
|
|
153
213
|
name: str = "JobShopInstance",
|
154
214
|
metadata: dict[str, Any] | None = None,
|
155
215
|
) -> JobShopInstance:
|
156
|
-
"""Creates a JobShopInstance from duration and machines
|
216
|
+
"""Creates a :class:`JobShopInstance` from duration and machines
|
217
|
+
matrices.
|
157
218
|
|
158
219
|
Args:
|
159
220
|
duration_matrix:
|
@@ -170,7 +231,7 @@ class JobShopInstance:
|
|
170
231
|
A dictionary with additional information about the instance.
|
171
232
|
|
172
233
|
Returns:
|
173
|
-
A JobShopInstance object.
|
234
|
+
A :class:`JobShopInstance` object.
|
174
235
|
"""
|
175
236
|
jobs: list[list[Operation]] = [[] for _ in range(len(duration_matrix))]
|
176
237
|
|
@@ -222,7 +283,7 @@ class JobShopInstance:
|
|
222
283
|
|
223
284
|
@functools.cached_property
|
224
285
|
def is_flexible(self) -> bool:
|
225
|
-
"""Returns True if any operation has more than one machine."""
|
286
|
+
"""Returns ``True`` if any operation has more than one machine."""
|
226
287
|
return any(
|
227
288
|
any(len(operation.machines) > 1 for operation in job)
|
228
289
|
for job in self.jobs
|
@@ -232,12 +293,14 @@ class JobShopInstance:
|
|
232
293
|
def durations_matrix(self) -> list[list[int]]:
|
233
294
|
"""Returns the duration matrix of the instance.
|
234
295
|
|
235
|
-
The duration of the operation with
|
236
|
-
is stored in the i-th position of the j-th list of the returned
|
296
|
+
The duration of the operation with ``job_id`` i and ``position_in_job``
|
297
|
+
j is stored in the i-th position of the j-th list of the returned
|
298
|
+
matrix:
|
299
|
+
|
300
|
+
.. code-block:: python
|
301
|
+
|
302
|
+
duration = instance.durations_matrix[i][j]
|
237
303
|
|
238
|
-
```python
|
239
|
-
duration = instance.durations_matrix[i][j]
|
240
|
-
```
|
241
304
|
"""
|
242
305
|
return [[operation.duration for operation in job] for job in self.jobs]
|
243
306
|
|
@@ -254,9 +317,9 @@ class JobShopInstance:
|
|
254
317
|
To access the machines of the operation with position i in the job
|
255
318
|
with id j, the following code must be used:
|
256
319
|
|
257
|
-
|
258
|
-
|
259
|
-
|
320
|
+
.. code-block:: python
|
321
|
+
|
322
|
+
machines = instance.machines_matrix[j][i]
|
260
323
|
|
261
324
|
"""
|
262
325
|
if self.is_flexible:
|
@@ -271,8 +334,9 @@ class JobShopInstance:
|
|
271
334
|
def durations_matrix_array(self) -> NDArray[np.float32]:
|
272
335
|
"""Returns the duration matrix of the instance as a numpy array.
|
273
336
|
|
274
|
-
The returned array has shape (num_jobs
|
275
|
-
|
337
|
+
The returned array has shape (``num_jobs``,
|
338
|
+
``max_num_operations_per_job``).
|
339
|
+
Non-existing operations are filled with ``np.nan``.
|
276
340
|
|
277
341
|
Example:
|
278
342
|
>>> jobs = [[Operation(0, 2), Operation(1, 3)], [Operation(0, 4)]]
|
@@ -288,9 +352,9 @@ class JobShopInstance:
|
|
288
352
|
def machines_matrix_array(self) -> NDArray[np.float32]:
|
289
353
|
"""Returns the machines matrix of the instance as a numpy array.
|
290
354
|
|
291
|
-
The returned array has shape (num_jobs
|
292
|
-
max_num_machines_per_operation).
|
293
|
-
np.nan
|
355
|
+
The returned array has shape (``num_jobs``,
|
356
|
+
``max_num_operations_per_job``, ``max_num_machines_per_operation``).
|
357
|
+
Non-existing machines are filled with ``np.nan``.
|
294
358
|
|
295
359
|
Example:
|
296
360
|
>>> jobs = [
|
@@ -413,7 +477,7 @@ class JobShopInstance:
|
|
413
477
|
def _fill_matrix_with_nans_2d(
|
414
478
|
matrix: list[list[int]],
|
415
479
|
) -> NDArray[np.float32]:
|
416
|
-
"""Fills a matrix with np.nan values.
|
480
|
+
"""Fills a matrix with ``np.nan`` values.
|
417
481
|
|
418
482
|
Args:
|
419
483
|
matrix:
|
@@ -421,7 +485,7 @@ class JobShopInstance:
|
|
421
485
|
|
422
486
|
Returns:
|
423
487
|
A numpy array with the same shape as the input matrix, filled with
|
424
|
-
np.nan values.
|
488
|
+
``np.nan`` values.
|
425
489
|
"""
|
426
490
|
max_length = max(len(row) for row in matrix)
|
427
491
|
squared_matrix = np.full(
|
@@ -435,7 +499,7 @@ class JobShopInstance:
|
|
435
499
|
def _fill_matrix_with_nans_3d(
|
436
500
|
matrix: list[list[list[int]]],
|
437
501
|
) -> NDArray[np.float32]:
|
438
|
-
"""Fills a 3D matrix with np.nan values.
|
502
|
+
"""Fills a 3D matrix with ``np.nan`` values.
|
439
503
|
|
440
504
|
Args:
|
441
505
|
matrix:
|
@@ -443,7 +507,7 @@ class JobShopInstance:
|
|
443
507
|
|
444
508
|
Returns:
|
445
509
|
A numpy array with the same shape as the input matrix, filled with
|
446
|
-
np.nan values.
|
510
|
+
``np.nan`` values.
|
447
511
|
"""
|
448
512
|
max_length = max(len(row) for row in matrix)
|
449
513
|
max_inner_length = len(matrix[0][0])
|
@@ -42,17 +42,28 @@ class Operation:
|
|
42
42
|
"The time it takes to perform the operation. Often referred"
|
43
43
|
" to as the processing time."
|
44
44
|
),
|
45
|
-
"job_id":
|
46
|
-
|
45
|
+
"job_id": (
|
46
|
+
"The id of the job the operation belongs to. Defaults to -1. "
|
47
|
+
"It is usually set by the :class:`JobShopInstance` class after "
|
48
|
+
"initialization."
|
49
|
+
),
|
50
|
+
"position_in_job": (
|
51
|
+
"The index of the operation in the job. Defaults to -1. "
|
52
|
+
"It is usually set by the :class:`JobShopInstance` class after "
|
53
|
+
"initialization."
|
54
|
+
),
|
47
55
|
"operation_id": (
|
48
56
|
"The id of the operation. This is unique within a "
|
49
|
-
":class:`JobShopInstance`."
|
57
|
+
":class:`JobShopInstance`. Defaults to -1. It is usually set by "
|
58
|
+
"the :class:`JobShopInstance` class after initialization."
|
50
59
|
),
|
51
60
|
}
|
52
61
|
|
53
62
|
def __init__(self, machines: int | list[int], duration: int):
|
54
|
-
self.machines
|
55
|
-
|
63
|
+
self.machines: list[int] = (
|
64
|
+
[machines] if isinstance(machines, int) else machines
|
65
|
+
)
|
66
|
+
self.duration: int = duration
|
56
67
|
|
57
68
|
# Defined outside the class by the JobShopInstance class:
|
58
69
|
self.job_id: int = -1
|
@@ -64,8 +75,8 @@ class Operation:
|
|
64
75
|
"""Returns the id of the machine associated with the operation.
|
65
76
|
|
66
77
|
Raises:
|
67
|
-
UninitializedAttributeError:
|
68
|
-
|
78
|
+
UninitializedAttributeError:
|
79
|
+
If the operation has multiple machines in its list.
|
69
80
|
"""
|
70
81
|
if len(self.machines) > 1:
|
71
82
|
raise UninitializedAttributeError(
|
@@ -25,6 +25,16 @@ class Schedule:
|
|
25
25
|
is_complete
|
26
26
|
add
|
27
27
|
reset
|
28
|
+
|
29
|
+
Args:
|
30
|
+
instance:
|
31
|
+
The :class:`JobShopInstance` object that the schedule is for.
|
32
|
+
schedule:
|
33
|
+
A list of lists of :class:`ScheduledOperation` objects. Each
|
34
|
+
list represents the order of operations on a machine. If
|
35
|
+
not provided, the schedule is initialized as an empty schedule.
|
36
|
+
**metadata:
|
37
|
+
Additional information about the schedule.
|
28
38
|
"""
|
29
39
|
|
30
40
|
__slots__ = {
|
@@ -46,28 +56,16 @@ class Schedule:
|
|
46
56
|
self,
|
47
57
|
instance: JobShopInstance,
|
48
58
|
schedule: list[list[ScheduledOperation]] | None = None,
|
49
|
-
**metadata,
|
59
|
+
**metadata: Any,
|
50
60
|
):
|
51
|
-
"""Initializes the object with the given instance and schedule.
|
52
|
-
|
53
|
-
Args:
|
54
|
-
instance:
|
55
|
-
The :class:`JobShopInstance` object that the schedule is for.
|
56
|
-
schedule:
|
57
|
-
A list of lists of :class:`ScheduledOperation` objects. Each
|
58
|
-
list represents the order of operations on a machine. If
|
59
|
-
not provided, the schedule is initialized as an empty schedule.
|
60
|
-
**metadata:
|
61
|
-
Additional information about the schedule.
|
62
|
-
"""
|
63
61
|
if schedule is None:
|
64
62
|
schedule = [[] for _ in range(instance.num_machines)]
|
65
63
|
|
66
64
|
Schedule.check_schedule(schedule)
|
67
65
|
|
68
|
-
self.instance = instance
|
66
|
+
self.instance: JobShopInstance = instance
|
69
67
|
self._schedule = schedule
|
70
|
-
self.metadata = metadata
|
68
|
+
self.metadata: dict[str, Any] = metadata
|
71
69
|
|
72
70
|
def __repr__(self) -> str:
|
73
71
|
return str(self.schedule)
|
@@ -5,7 +5,21 @@ from job_shop_lib.exceptions import ValidationError
|
|
5
5
|
|
6
6
|
|
7
7
|
class ScheduledOperation:
|
8
|
-
"""Data structure to store a scheduled operation.
|
8
|
+
"""Data structure to store a scheduled operation.
|
9
|
+
|
10
|
+
Args:
|
11
|
+
operation:
|
12
|
+
The :class:`Operation` object that is scheduled.
|
13
|
+
start_time:
|
14
|
+
The time at which the operation is scheduled to start.
|
15
|
+
machine_id:
|
16
|
+
The id of the machine on which the operation is scheduled.
|
17
|
+
|
18
|
+
Raises:
|
19
|
+
ValidationError:
|
20
|
+
If the given machine_id is not in the list of valid machines
|
21
|
+
for the operation.
|
22
|
+
"""
|
9
23
|
|
10
24
|
__slots__ = {
|
11
25
|
"operation": "The :class:`Operation` object that is scheduled.",
|
@@ -16,23 +30,8 @@ class ScheduledOperation:
|
|
16
30
|
}
|
17
31
|
|
18
32
|
def __init__(self, operation: Operation, start_time: int, machine_id: int):
|
19
|
-
|
20
|
-
|
21
|
-
Args:
|
22
|
-
operation:
|
23
|
-
The :class:`Operation` object that is scheduled.
|
24
|
-
start_time:
|
25
|
-
The time at which the operation is scheduled to start.
|
26
|
-
machine_id:
|
27
|
-
The id of the machine on which the operation is scheduled.
|
28
|
-
|
29
|
-
Raises:
|
30
|
-
ValidationError:
|
31
|
-
If the given machine_id is not in the list of valid machines
|
32
|
-
for the operation.
|
33
|
-
"""
|
34
|
-
self.operation = operation
|
35
|
-
self.start_time = start_time
|
33
|
+
self.operation: Operation = operation
|
34
|
+
self.start_time: int = start_time
|
36
35
|
self._machine_id = machine_id
|
37
36
|
self.machine_id = machine_id # Validate machine_id
|
38
37
|
|
@@ -32,6 +32,8 @@ from ._ready_operation_filters import (
|
|
32
32
|
filter_dominated_operations,
|
33
33
|
filter_non_immediate_machines,
|
34
34
|
ReadyOperationsFilter,
|
35
|
+
filter_non_idle_machines,
|
36
|
+
filter_non_immediate_operations,
|
35
37
|
)
|
36
38
|
from ._dispatcher_observer_config import DispatcherObserverConfig
|
37
39
|
from ._factories import (
|
@@ -53,4 +55,6 @@ __all__ = [
|
|
53
55
|
"DispatcherObserverConfig",
|
54
56
|
"UnscheduledOperationsObserver",
|
55
57
|
"ReadyOperationsFilter",
|
58
|
+
"filter_non_idle_machines",
|
59
|
+
"filter_non_immediate_operations",
|
56
60
|
]
|
@@ -29,6 +29,18 @@ class DispatcherObserver(abc.ABC):
|
|
29
29
|
dispatcher:
|
30
30
|
The :class:`Dispatcher` instance to observe.
|
31
31
|
|
32
|
+
Args:
|
33
|
+
dispatcher:
|
34
|
+
The :class:`Dispatcher` instance to observe.
|
35
|
+
subscribe:
|
36
|
+
If ``True``, automatically subscribes the observer to the
|
37
|
+
dispatcher when it is initialized. Defaults to ``True``.
|
38
|
+
|
39
|
+
Raises:
|
40
|
+
ValidationError: If ``is_singleton`` is ``True`` and an observer of the
|
41
|
+
same type already exists in the dispatcher's list of
|
42
|
+
subscribers.
|
43
|
+
|
32
44
|
Example:
|
33
45
|
|
34
46
|
.. code-block:: python
|
@@ -61,21 +73,6 @@ class DispatcherObserver(abc.ABC):
|
|
61
73
|
*,
|
62
74
|
subscribe: bool = True,
|
63
75
|
):
|
64
|
-
"""Initializes the observer with the :class:`Dispatcher` and subscribes
|
65
|
-
to it.
|
66
|
-
|
67
|
-
Args:
|
68
|
-
dispatcher:
|
69
|
-
The `Dispatcher` instance to observe.
|
70
|
-
subscribe:
|
71
|
-
If True, automatically subscribes the observer to the
|
72
|
-
dispatcher.
|
73
|
-
|
74
|
-
Raises:
|
75
|
-
ValidationError: If ``is_singleton`` is True and an observer of the
|
76
|
-
same type already exists in the dispatcher's list of
|
77
|
-
subscribers.
|
78
|
-
"""
|
79
76
|
if self._is_singleton and any(
|
80
77
|
isinstance(observer, self.__class__)
|
81
78
|
for observer in dispatcher.subscribers
|
@@ -156,26 +153,30 @@ class Dispatcher:
|
|
156
153
|
responsible for scheduling the operations on the machines and keeping
|
157
154
|
track of the next available time for each machine and job.
|
158
155
|
|
159
|
-
|
156
|
+
Args:
|
160
157
|
instance:
|
161
|
-
The instance of the job shop problem to be
|
162
|
-
schedule:
|
163
|
-
The schedule of operations on machines.
|
158
|
+
The instance of the job shop problem to be solved.
|
164
159
|
ready_operations_filter:
|
165
|
-
A function that filters out operations that are not ready to
|
166
|
-
scheduled.
|
160
|
+
A function that filters out operations that are not ready to
|
161
|
+
be scheduled. The function should take the dispatcher and a
|
162
|
+
list of operations as input and return a list of operations
|
163
|
+
that are ready to be scheduled. If ``None``, no filtering is
|
164
|
+
done.
|
167
165
|
"""
|
168
166
|
|
169
|
-
__slots__ =
|
170
|
-
"instance",
|
171
|
-
"schedule",
|
172
|
-
"_machine_next_available_time",
|
173
|
-
"_job_next_operation_index",
|
174
|
-
"_job_next_available_time",
|
175
|
-
"ready_operations_filter"
|
176
|
-
|
177
|
-
|
178
|
-
|
167
|
+
__slots__ = {
|
168
|
+
"instance": "The instance of the job shop problem to be scheduled.",
|
169
|
+
"schedule": "The schedule of operations on machines.",
|
170
|
+
"_machine_next_available_time": "",
|
171
|
+
"_job_next_operation_index": "",
|
172
|
+
"_job_next_available_time": "",
|
173
|
+
"ready_operations_filter": (
|
174
|
+
"A function that filters out operations that are not ready to be "
|
175
|
+
"scheduled."
|
176
|
+
),
|
177
|
+
"subscribers": "A list of observers subscribed to the dispatcher.",
|
178
|
+
"_cache": "A dictionary to cache the results of the cached methods.",
|
179
|
+
}
|
179
180
|
|
180
181
|
def __init__(
|
181
182
|
self,
|
@@ -184,18 +185,6 @@ class Dispatcher:
|
|
184
185
|
Callable[[Dispatcher, list[Operation]], list[Operation]] | None
|
185
186
|
) = None,
|
186
187
|
) -> None:
|
187
|
-
"""Initializes the object with the given instance.
|
188
|
-
|
189
|
-
Args:
|
190
|
-
instance:
|
191
|
-
The instance of the job shop problem to be solved.
|
192
|
-
ready_operations_filter:
|
193
|
-
A function that filters out operations that are not ready to
|
194
|
-
be scheduled. The function should take the dispatcher and a
|
195
|
-
list of operations as input and return a list of operations
|
196
|
-
that are ready to be scheduled. If ``None``, no filtering is
|
197
|
-
done.
|
198
|
-
"""
|
199
188
|
|
200
189
|
self.instance = instance
|
201
190
|
self.schedule = Schedule(self.instance)
|
@@ -371,7 +360,7 @@ class Dispatcher:
|
|
371
360
|
The current time is the minimum start time of the available
|
372
361
|
operations.
|
373
362
|
"""
|
374
|
-
available_operations = self.
|
363
|
+
available_operations = self.available_operations()
|
375
364
|
current_time = self.min_start_time(available_operations)
|
376
365
|
return current_time
|
377
366
|
|
@@ -387,7 +376,7 @@ class Dispatcher:
|
|
387
376
|
return int(min_start_time)
|
388
377
|
|
389
378
|
@_dispatcher_cache
|
390
|
-
def
|
379
|
+
def available_operations(self) -> list[Operation]:
|
391
380
|
"""Returns a list of available operations for processing, optionally
|
392
381
|
filtering out operations using the filter function.
|
393
382
|
|
@@ -443,7 +432,7 @@ class Dispatcher:
|
|
443
432
|
@_dispatcher_cache
|
444
433
|
def available_machines(self) -> list[int]:
|
445
434
|
"""Returns the list of ready machines."""
|
446
|
-
available_operations = self.
|
435
|
+
available_operations = self.available_operations()
|
447
436
|
available_machines = set()
|
448
437
|
for operation in available_operations:
|
449
438
|
available_machines.update(operation.machines)
|
@@ -452,7 +441,7 @@ class Dispatcher:
|
|
452
441
|
@_dispatcher_cache
|
453
442
|
def available_jobs(self) -> list[int]:
|
454
443
|
"""Returns the list of ready jobs."""
|
455
|
-
available_operations = self.
|
444
|
+
available_operations = self.available_operations()
|
456
445
|
available_jobs = set(
|
457
446
|
operation.job_id for operation in available_operations
|
458
447
|
)
|