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.
Files changed (69) hide show
  1. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/PKG-INFO +15 -3
  2. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/README.md +14 -2
  3. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_job_shop_instance.py +119 -55
  4. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_operation.py +18 -7
  5. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_schedule.py +13 -15
  6. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_scheduled_operation.py +17 -18
  7. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/__init__.py +4 -0
  8. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_dispatcher.py +36 -47
  9. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_dispatcher_observer_config.py +15 -2
  10. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_factories.py +10 -2
  11. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_ready_operation_filters.py +80 -0
  12. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +0 -1
  13. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_factory.py +21 -18
  14. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +1 -0
  15. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +1 -1
  16. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +44 -25
  17. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -9
  18. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/_general_instance_generator.py +33 -34
  19. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/_instance_generator.py +14 -17
  20. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/_transformations.py +11 -8
  21. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/__init__.py +3 -0
  22. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_build_disjunctive_graph.py +41 -3
  23. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +11 -13
  24. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +17 -20
  25. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/__init__.py +16 -7
  26. {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
  27. {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
  28. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_types_and_constants.py +2 -2
  29. job_shop_lib-1.0.0a4/job_shop_lib/visualization/__init__.py +60 -0
  30. job_shop_lib-1.0.0a4/job_shop_lib/visualization/_gantt_chart_creator.py +257 -0
  31. {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
  32. job_shop_lib-1.0.0a4/job_shop_lib/visualization/_plot_disjunctive_graph.py +382 -0
  33. 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
  34. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/pyproject.toml +2 -1
  35. job_shop_lib-1.0.0a2/job_shop_lib/visualization/__init__.py +0 -41
  36. job_shop_lib-1.0.0a2/job_shop_lib/visualization/_disjunctive_graph.py +0 -210
  37. job_shop_lib-1.0.0a2/job_shop_lib/visualization/_gantt_chart_creator.py +0 -219
  38. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/LICENSE +0 -0
  39. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/__init__.py +0 -0
  40. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/_base_solver.py +0 -0
  41. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/benchmarking/__init__.py +0 -0
  42. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/benchmarking/_load_benchmark.py +0 -0
  43. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
  44. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/constraint_programming/__init__.py +0 -0
  45. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/constraint_programming/_ortools_solver.py +0 -0
  46. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_history_observer.py +0 -0
  47. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +0 -0
  48. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
  49. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +0 -0
  50. {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
  51. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +0 -0
  52. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
  53. {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
  54. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
  55. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/__init__.py +0 -0
  56. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +0 -0
  57. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +0 -0
  58. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/dispatching/rules/_utils.py +0 -0
  59. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/exceptions.py +0 -0
  60. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/generation/__init__.py +0 -0
  61. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_build_agent_task_graph.py +0 -0
  62. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_constants.py +0 -0
  63. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_job_shop_graph.py +0 -0
  64. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/_node.py +0 -0
  65. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
  66. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
  67. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_reward_observers.py +0 -0
  68. {job_shop_lib-1.0.0a2 → job_shop_lib-1.0.0a4}/job_shop_lib/reinforcement_learning/_utils.py +0 -0
  69. /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.0a2
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
  [![Tests](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml/badge.svg)](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml)
32
+ [![Documentation Status](https://readthedocs.org/projects/job-shop-lib/badge/?version=latest)](https://job-shop-lib.readthedocs.io/en/latest/?badge=latest)
32
33
  ![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)
33
34
  [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
34
35
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide!
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
  [![Tests](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml/badge.svg)](https://github.com/Pabloo22/job_shop_lib/actions/workflows/tests.yaml)
8
+ [![Documentation Status](https://readthedocs.org/projects/job-shop-lib/badge/?version=latest)](https://job-shop-lib.readthedocs.io/en/latest/?badge=latest)
8
9
  ![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)
9
10
  [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
10
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide!
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 `num_jobs` or `num_machines` can be computed
19
- from the instance and are cached for performance if they require expensive
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 `job_id`, `position_in_job`, and `operation_id` attributes of
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
- """Initializes the instance based on a list of lists of operations.
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 file,
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
- The returned dictionary has the following structure:
134
- {
135
- "name": self.name,
136
- "duration_matrix": self.durations_matrix,
137
- "machines_matrix": self.machines_matrix,
138
- "metadata": self.metadata,
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 matrices.
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 `job_id` i and `position_in_job` j
236
- is stored in the i-th position of the j-th list of the returned matrix:
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
- ```python
258
- machines = instance.machines_matrix[j][i]
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, max_num_operations_per_job).
275
- Non-existing operations are filled with np.nan.
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, max_num_operations_per_job,
292
- max_num_machines_per_operation). Non-existing machines are filled with
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": "The id of the job the operation belongs to.",
46
- "position_in_job": "The index of the operation in the job.",
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 = [machines] if isinstance(machines, int) else machines
55
- self.duration = duration
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: If the operation has multiple machines
68
- in its list.
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
- """Initializes a new instance of the :class:`ScheduledOperation` class.
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
- Attributes:
156
+ Args:
160
157
  instance:
161
- The instance of the job shop problem to be scheduled.
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 be
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
- "subscribers",
177
- "_cache",
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.ready_operations()
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 ready_operations(self) -> list[Operation]:
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.ready_operations()
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.ready_operations()
444
+ available_operations = self.available_operations()
456
445
  available_jobs = set(
457
446
  operation.job_id for operation in available_operations
458
447
  )