job-shop-lib 1.0.0b5__tar.gz → 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/PKG-INFO +19 -18
  2. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/README.md +17 -16
  3. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/__init__.py +1 -1
  4. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/_job_shop_instance.py +2 -2
  5. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/_operation.py +9 -3
  6. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/_scheduled_operation.py +3 -0
  7. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/benchmarking/__init__.py +1 -0
  8. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/__init__.py +12 -10
  9. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_dispatcher.py +6 -13
  10. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_factories.py +3 -3
  11. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_optimal_operations_observer.py +0 -2
  12. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_ready_operation_filters.py +4 -4
  13. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +11 -6
  14. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_factory.py +8 -3
  15. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +1 -1
  16. job_shop_lib-1.0.2/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +97 -0
  17. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/rules/__init__.py +11 -8
  18. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +1 -1
  19. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +3 -2
  20. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/generation/__init__.py +12 -1
  21. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/__init__.py +42 -8
  22. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/_build_resource_task_graphs.py +1 -1
  23. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/_job_shop_graph.py +38 -19
  24. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/graph_updaters/__init__.py +5 -1
  25. job_shop_lib-1.0.2/job_shop_lib/graphs/graph_updaters/_disjunctive_graph_updater.py +108 -0
  26. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +3 -1
  27. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/graph_updaters/_utils.py +2 -2
  28. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/__init__.py +13 -7
  29. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +1 -1
  30. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py +102 -24
  31. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +11 -2
  32. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/_types_and_constants.py +11 -10
  33. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/_utils.py +29 -0
  34. job_shop_lib-1.0.2/job_shop_lib/visualization/__init__.py +0 -0
  35. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/gantt/__init__.py +7 -3
  36. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py +5 -2
  37. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/graphs/__init__.py +1 -0
  38. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py +53 -19
  39. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/pyproject.toml +5 -12
  40. job_shop_lib-1.0.0b5/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +0 -129
  41. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/LICENSE +0 -0
  42. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/_base_solver.py +0 -0
  43. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/_schedule.py +0 -0
  44. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/benchmarking/_load_benchmark.py +0 -0
  45. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
  46. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/constraint_programming/__init__.py +0 -0
  47. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/constraint_programming/_ortools_solver.py +0 -0
  48. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_dispatcher_observer_config.py +0 -0
  49. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_history_observer.py +0 -0
  50. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +0 -0
  51. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
  52. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +0 -0
  53. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +0 -0
  54. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +0 -0
  55. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
  56. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py +0 -0
  57. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
  58. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +0 -0
  59. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +0 -0
  60. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/dispatching/rules/_utils.py +0 -0
  61. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/exceptions.py +0 -0
  62. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/generation/_general_instance_generator.py +0 -0
  63. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/generation/_instance_generator.py +0 -0
  64. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/generation/_transformations.py +0 -0
  65. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/generation/_utils.py +0 -0
  66. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/_build_disjunctive_graph.py +0 -0
  67. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/_constants.py +0 -0
  68. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/_node.py +0 -0
  69. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +0 -0
  70. /job_shop_lib-1.0.0b5/job_shop_lib/visualization/__init__.py → /job_shop_lib-1.0.2/job_shop_lib/py.typed +0 -0
  71. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/reinforcement_learning/_reward_observers.py +0 -0
  72. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/gantt/_gantt_chart_creator.py +0 -0
  73. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/gantt/_plot_gantt_chart.py +0 -0
  74. {job_shop_lib-1.0.0b5 → job_shop_lib-1.0.2}/job_shop_lib/visualization/graphs/_plot_resource_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.0b5
3
+ Version: 1.0.2
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
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Provides-Extra: pygraphviz
15
- Requires-Dist: gymnasium (>=0.29.1,<0.30.0)
15
+ Requires-Dist: gymnasium (>=1.0.0,<2.0.0)
16
16
  Requires-Dist: imageio[ffmpeg] (>=2.34.1,<3.0.0)
17
17
  Requires-Dist: matplotlib (>=3,<4)
18
18
  Requires-Dist: networkx (>=3,<4)
@@ -40,7 +40,11 @@ JobShopLib is a Python package for creating, solving, and visualizing job shop s
40
40
 
41
41
  It follows a modular design, allowing users to easily extend the library with new solvers, dispatching rules, visualization functions, etc.
42
42
 
43
- See the [documentation](https://job-shop-lib.readthedocs.io/en/latest/) for more details about the latest version.
43
+ There is a [documentation page](https://job-shop-lib.readthedocs.io/en/stable/) for versions 1.0.0a3 and onward. See the [latest pull requests](https://github.com/Pabloo22/job_shop_lib/pulls?q=is%3Apr+is%3Aclosed) for the latest changes.
44
+
45
+ See [`gnn_scheduler`](https://github.com/Pabloo22/gnn_scheduler/blob/main/gnn_scheduler/) for an example implementation of a graph neural network-based dispatcher trained with [PyTorch Geometric](https://pyg.org/).
46
+
47
+ See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide! More advanced examples can be found [here](https://job-shop-lib.readthedocs.io/en/stable/examples.html).
44
48
 
45
49
  ## Installation :package:
46
50
 
@@ -48,23 +52,12 @@ See the [documentation](https://job-shop-lib.readthedocs.io/en/latest/) for more
48
52
 
49
53
  JobShopLib is distributed on [PyPI](https://pypi.org/project/job-shop-lib/) and it supports Python 3.10+.
50
54
 
51
- You can install the latest stable version (version 0.5.1) using `pip`:
55
+ You can install the latest stable version using `pip`:
52
56
 
53
57
  ```bash
54
58
  pip install job-shop-lib
55
59
  ```
56
60
 
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 beta stage and can be installed with:
61
-
62
- ```bash
63
- pip install job-shop-lib==1.0.0b5
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)). There is a [documentation page](https://job-shop-lib.readthedocs.io/en/latest/) for versions 1.0.0a3 and onward.
67
-
68
61
  <!-- end installation -->
69
62
 
70
63
  <!-- key features -->
@@ -258,7 +251,15 @@ Additionally, the graph includes **disjunctive edges** between operations that u
258
251
  ```python
259
252
  from job_shop_lib.visualization import plot_disjunctive_graph
260
253
 
261
- fig = plot_disjunctive_graph(instance)
254
+ fig = plot_disjunctive_graph(
255
+ instance,
256
+ figsize=(6, 4),
257
+ draw_disjunctive_edges="single_edge",
258
+ disjunctive_edges_additional_params={
259
+ "arrowstyle": "<|-|>",
260
+ "connectionstyle": "arc3,rad=0.15",
261
+ },
262
+ )
262
263
  plt.show()
263
264
  ```
264
265
 
@@ -314,9 +315,9 @@ from job_shop_lib.graphs import (
314
315
  )
315
316
  from job_shop_lib.visualization import plot_resource_task_graph
316
317
 
317
- complete_resource_task_graph = build_complete_resource_task_graph(instance)
318
+ resource_task_graph = build_resource_task_graph(instance)
318
319
 
319
- fig = plot_resource_task_graph(complete_agent_task_graph)
320
+ fig = plot_resource_task_graph(resource_task_graph)
320
321
  plt.show()
321
322
  ```
322
323
 
@@ -16,7 +16,11 @@ JobShopLib is a Python package for creating, solving, and visualizing job shop s
16
16
 
17
17
  It follows a modular design, allowing users to easily extend the library with new solvers, dispatching rules, visualization functions, etc.
18
18
 
19
- See the [documentation](https://job-shop-lib.readthedocs.io/en/latest/) for more details about the latest version.
19
+ There is a [documentation page](https://job-shop-lib.readthedocs.io/en/stable/) for versions 1.0.0a3 and onward. See the [latest pull requests](https://github.com/Pabloo22/job_shop_lib/pulls?q=is%3Apr+is%3Aclosed) for the latest changes.
20
+
21
+ See [`gnn_scheduler`](https://github.com/Pabloo22/gnn_scheduler/blob/main/gnn_scheduler/) for an example implementation of a graph neural network-based dispatcher trained with [PyTorch Geometric](https://pyg.org/).
22
+
23
+ See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide! More advanced examples can be found [here](https://job-shop-lib.readthedocs.io/en/stable/examples.html).
20
24
 
21
25
  ## Installation :package:
22
26
 
@@ -24,23 +28,12 @@ See the [documentation](https://job-shop-lib.readthedocs.io/en/latest/) for more
24
28
 
25
29
  JobShopLib is distributed on [PyPI](https://pypi.org/project/job-shop-lib/) and it supports Python 3.10+.
26
30
 
27
- You can install the latest stable version (version 0.5.1) using `pip`:
31
+ You can install the latest stable version using `pip`:
28
32
 
29
33
  ```bash
30
34
  pip install job-shop-lib
31
35
  ```
32
36
 
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 beta stage and can be installed with:
37
-
38
- ```bash
39
- pip install job-shop-lib==1.0.0b5
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)). There is a [documentation page](https://job-shop-lib.readthedocs.io/en/latest/) for versions 1.0.0a3 and onward.
43
-
44
37
  <!-- end installation -->
45
38
 
46
39
  <!-- key features -->
@@ -234,7 +227,15 @@ Additionally, the graph includes **disjunctive edges** between operations that u
234
227
  ```python
235
228
  from job_shop_lib.visualization import plot_disjunctive_graph
236
229
 
237
- fig = plot_disjunctive_graph(instance)
230
+ fig = plot_disjunctive_graph(
231
+ instance,
232
+ figsize=(6, 4),
233
+ draw_disjunctive_edges="single_edge",
234
+ disjunctive_edges_additional_params={
235
+ "arrowstyle": "<|-|>",
236
+ "connectionstyle": "arc3,rad=0.15",
237
+ },
238
+ )
238
239
  plt.show()
239
240
  ```
240
241
 
@@ -290,9 +291,9 @@ from job_shop_lib.graphs import (
290
291
  )
291
292
  from job_shop_lib.visualization import plot_resource_task_graph
292
293
 
293
- complete_resource_task_graph = build_complete_resource_task_graph(instance)
294
+ resource_task_graph = build_resource_task_graph(instance)
294
295
 
295
- fig = plot_resource_task_graph(complete_agent_task_graph)
296
+ fig = plot_resource_task_graph(resource_task_graph)
296
297
  plt.show()
297
298
  ```
298
299
 
@@ -19,7 +19,7 @@ from job_shop_lib._schedule import Schedule
19
19
  from job_shop_lib._base_solver import BaseSolver, Solver
20
20
 
21
21
 
22
- __version__ = "1.0.0-b.5"
22
+ __version__ = "1.0.2"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
@@ -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 if they
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
 
@@ -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 UninitializedAttributeError
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 UninitializedAttributeError(
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
 
@@ -80,3 +80,6 @@ class ScheduledOperation:
80
80
  and self.start_time == value.start_time
81
81
  and self.machine_id == value.machine_id
82
82
  )
83
+
84
+ def __hash__(self) -> int:
85
+ return hash((self.operation, self.start_time, self.machine_id))
@@ -1,6 +1,7 @@
1
1
  """Contains functions to load benchmark instances.
2
2
 
3
3
  .. autosummary::
4
+ :nosignatures:
4
5
 
5
6
  load_all_benchmark_instances
6
7
  load_benchmark_instance
@@ -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
- "OptimalOperationsObserver",
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. If ``None``, the start time is computed based on the
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[Operation]:
461
+ def scheduled_operations(self) -> List[ScheduledOperation]:
463
462
  """Returns the list of operations that have been scheduled."""
464
463
  scheduled_operations = []
465
- for job_id, next_position in enumerate(self._job_next_operation_index):
466
- operations = self.instance.jobs[job_id][:next_position]
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[Operation]:
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. Supported
99
- values are 'dominated_operations' and
100
- 'immediate_machine_operations'.
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
- self: Dispatcher, available_operations: List[Operation]
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] * self.instance.num_machines
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 = self.min_start_time(available_operations)
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 self.start_time(op, machine_id) == current_time:
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
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[FeatureObserverConfig],
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
- Union[Type[FeatureObserver], FeatureObserverType, str]
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``. This is because most machine
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``
@@ -0,0 +1,97 @@
1
+ """Home of the `IsCompletedObserver` class."""
2
+
3
+ from typing import Optional, Union, List
4
+ import numpy as np
5
+
6
+ from job_shop_lib import ScheduledOperation
7
+ from job_shop_lib.dispatching import Dispatcher
8
+ from job_shop_lib.dispatching.feature_observers import (
9
+ FeatureObserver,
10
+ FeatureType,
11
+ )
12
+
13
+
14
+ class IsCompletedObserver(FeatureObserver):
15
+ """Adds a binary feature indicating whether each operation,
16
+ machine, or job has been completed.
17
+
18
+ An operation is considered completed if it has been scheduled and the
19
+ current time is greater than or equal to the sum of the operation's start
20
+ time and duration.
21
+
22
+ A machine or job is considered completed if all of its operations have been
23
+ completed.
24
+
25
+ Args:
26
+ dispatcher:
27
+ The :class:`~job_shop_lib.dispatching.Dispatcher` to observe.
28
+ feature_types:
29
+ A list of :class:`FeatureType` or a single :class:`FeatureType`
30
+ that specifies the types of features to observe. They must be a
31
+ subset of the class attribute :attr:`supported_feature_types`.
32
+ If ``None``, all supported feature types are tracked.
33
+ subscribe:
34
+ If ``True``, the observer is subscribed to the dispatcher upon
35
+ initialization. Otherwise, the observer must be subscribed later
36
+ or manually updated.
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ dispatcher: Dispatcher,
42
+ *,
43
+ feature_types: Optional[Union[List[FeatureType], FeatureType]] = None,
44
+ subscribe: bool = True,
45
+ ):
46
+ feature_types = self._get_feature_types_list(feature_types)
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
+ ]
54
+ )
55
+ self._num_of_operations_per_job = np.array(
56
+ [len(job) for job in dispatcher.instance.jobs]
57
+ )
58
+ super().__init__(
59
+ dispatcher,
60
+ feature_types=feature_types,
61
+ subscribe=subscribe,
62
+ )
63
+
64
+ def initialize_features(self):
65
+ if FeatureType.OPERATIONS in self.features:
66
+ completed_operations = [
67
+ op.operation.operation_id
68
+ for op in self.dispatcher.completed_operations()
69
+ ]
70
+ self.features[FeatureType.OPERATIONS][completed_operations, 0] = 1
71
+ if FeatureType.MACHINES in self.features:
72
+ num_completed_ops_per_machine = np.zeros(
73
+ len(self._num_of_operations_per_machine)
74
+ )
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)
82
+ if FeatureType.JOBS in self.features:
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
- ValueError: If the machine_chooser argument is not recognized or is
51
- not supported.
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,