job-shop-lib 1.0.0a5__tar.gz → 1.0.0b2__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/PKG-INFO +21 -15
  2. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/README.md +20 -14
  3. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/__init__.py +1 -1
  4. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_job_shop_instance.py +34 -29
  5. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_operation.py +4 -2
  6. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_schedule.py +11 -11
  7. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/benchmarking/_load_benchmark.py +3 -3
  8. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/constraint_programming/_ortools_solver.py +6 -6
  9. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/__init__.py +4 -3
  10. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_dispatcher.py +19 -19
  11. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_dispatcher_observer_config.py +4 -4
  12. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_factories.py +4 -2
  13. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_history_observer.py +2 -1
  14. job_shop_lib-1.0.0b2/job_shop_lib/dispatching/_optimal_operations_observer.py +115 -0
  15. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_ready_operation_filters.py +19 -18
  16. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +4 -3
  17. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +7 -8
  18. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +3 -1
  19. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_factory.py +13 -14
  20. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +9 -8
  21. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +2 -1
  22. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +4 -2
  23. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/__init__.py +37 -1
  24. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +4 -2
  25. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +50 -20
  26. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -8
  27. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +4 -3
  28. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_utils.py +9 -8
  29. job_shop_lib-1.0.0b2/job_shop_lib/generation/__init__.py +19 -0
  30. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/generation/_general_instance_generator.py +42 -64
  31. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/generation/_instance_generator.py +11 -7
  32. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/generation/_transformations.py +5 -4
  33. job_shop_lib-1.0.0b2/job_shop_lib/generation/_utils.py +124 -0
  34. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/__init__.py +7 -7
  35. job_shop_lib-1.0.0a5/job_shop_lib/graphs/_build_agent_task_graph.py → job_shop_lib-1.0.0b2/job_shop_lib/graphs/_build_resource_task_graphs.py +26 -24
  36. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_job_shop_graph.py +17 -13
  37. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_node.py +6 -4
  38. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +4 -2
  39. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +40 -20
  40. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_reward_observers.py +3 -1
  41. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +89 -22
  42. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_types_and_constants.py +1 -1
  43. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_utils.py +3 -3
  44. job_shop_lib-1.0.0b2/job_shop_lib/visualization/__init__.py +0 -0
  45. {job_shop_lib-1.0.0a5/job_shop_lib/visualization → job_shop_lib-1.0.0b2/job_shop_lib/visualization/gantt}/__init__.py +5 -17
  46. {job_shop_lib-1.0.0a5/job_shop_lib/visualization → job_shop_lib-1.0.0b2/job_shop_lib/visualization/gantt}/_gantt_chart_creator.py +12 -12
  47. {job_shop_lib-1.0.0a5/job_shop_lib/visualization → job_shop_lib-1.0.0b2/job_shop_lib/visualization/gantt}/_gantt_chart_video_and_gif_creation.py +22 -22
  48. {job_shop_lib-1.0.0a5/job_shop_lib/visualization → job_shop_lib-1.0.0b2/job_shop_lib/visualization/gantt}/_plot_gantt_chart.py +12 -13
  49. job_shop_lib-1.0.0b2/job_shop_lib/visualization/graphs/__init__.py +29 -0
  50. {job_shop_lib-1.0.0a5/job_shop_lib/visualization → job_shop_lib-1.0.0b2/job_shop_lib/visualization/graphs}/_plot_disjunctive_graph.py +18 -16
  51. job_shop_lib-1.0.0b2/job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +389 -0
  52. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/pyproject.toml +3 -1
  53. job_shop_lib-1.0.0a5/job_shop_lib/generation/__init__.py +0 -11
  54. job_shop_lib-1.0.0a5/job_shop_lib/visualization/_plot_agent_task_graph.py +0 -276
  55. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/LICENSE +0 -0
  56. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_base_solver.py +0 -0
  57. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_scheduled_operation.py +0 -0
  58. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/benchmarking/__init__.py +0 -0
  59. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
  60. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/constraint_programming/__init__.py +0 -0
  61. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
  62. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +0 -0
  63. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
  64. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py +0 -0
  65. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
  66. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/exceptions.py +0 -0
  67. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_build_disjunctive_graph.py +0 -0
  68. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_constants.py +0 -0
  69. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
  70. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +0 -0
  71. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
  72. {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 1.0.0a5
3
+ Version: 1.0.0b2
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
@@ -36,7 +36,7 @@ Description-Content-Type: text/markdown
36
36
 
37
37
  </div>
38
38
 
39
- JobShopLib is a Python package for creating, solving, and visualizing Job Shop Scheduling Problems (JSSP).
39
+ JobShopLib is a Python package for creating, solving, and visualizing job shop scheduling problems (JSSP).
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
 
@@ -57,10 +57,10 @@ pip install job-shop-lib
57
57
  See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide!
58
58
 
59
59
 
60
- Version 1.0.0 is currently in alpha stage and can be installed with:
60
+ Version 1.0.0 is currently in beta stage and can be installed with:
61
61
 
62
62
  ```bash
63
- pip install job-shop-lib==1.0.0a5
63
+ pip install job-shop-lib==1.0.0b2
64
64
  ```
65
65
 
66
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.
@@ -89,7 +89,11 @@ Although this version is not stable and may contain breaking changes in subseque
89
89
  - **Agent-Task Graphs**: Encode instances as agent-task graphs (introduced in [ScheduleNet paper](https://arxiv.org/abs/2106.03051)). See [Agent-Task Graph](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/07-Agent-Task-Graph.ipynb).
90
90
  - Build your own custom graphs with the `JobShopGraph` class.
91
91
 
92
+ <<<<<<< HEAD
93
+ - **Gymnasium Environments**: Two environments for solving the problem with graph neural networks (GNNs) or any other method, and reinforcement learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
94
+ =======
92
95
  - **Gymnasium Environments**: Two environments for solving the problem with Graph Neural Networks (GNNs) or any other method, and Reinforcement Learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
96
+ >>>>>>> 031bdf3 ([Docs] Update links in README to point to the correct GitHub URLs for tutorials and examples)
93
97
 
94
98
  <!-- end key features -->
95
99
 
@@ -245,7 +249,7 @@ The dashed red line represents the current time step, which is computed as the e
245
249
 
246
250
  ### Representing Instances as Graphs
247
251
 
248
- One of the main purposes of this library is to provide an easy way to encode instances as graphs. This can be very useful, not only for visualization purposes but also for developing Graph Neural Network-based algorithms.
252
+ One of the main purposes of this library is to provide an easy way to encode instances as graphs. This can be very useful, not only for visualization purposes but also for developing graph neural network-based algorithms.
249
253
 
250
254
  A graph is represented by the `JobShopGraph` class, which internally stores a `networkx.DiGraph` object.
251
255
 
@@ -253,7 +257,7 @@ A graph is represented by the `JobShopGraph` class, which internally stores a `n
253
257
 
254
258
  The disjunctive graph is created by first adding nodes representing each operation in the jobs, along with two special nodes: a source $S$ and a sink $T$. Each operation node is linked to the next operation in its job sequence by **conjunctive edges**, forming a path from the source to the sink. These edges represent the order in which operations of a single job must be performed.
255
259
 
256
- Additionally, the graph includes **disjunctive edges** between operations that use the same machine but belong to different jobs. These edges are bidirectional, indicating that either of the connected operations can be performed first. The disjunctive edges thus represent the scheduling choices available: the order in which operations sharing a machine can be processed. Solving the Job Shop Scheduling problem involves choosing a direction for each disjunctive edge such that the overall processing time is minimized.
260
+ Additionally, the graph includes **disjunctive edges** between operations that use the same machine but belong to different jobs. These edges are bidirectional, indicating that either of the connected operations can be performed first. The disjunctive edges thus represent the scheduling choices available: the order in which operations sharing a machine can be processed. Solving the job shop scheduling problem involves choosing a direction for each disjunctive edge such that the overall processing time is minimized.
257
261
 
258
262
  ```python
259
263
  from job_shop_lib.visualization import plot_disjunctive_graph
@@ -295,9 +299,9 @@ Other attributes include:
295
299
  - `nodes_by_machine`: A nested list mapping each machine to its associated operation nodes, aiding in machine-specific analysis.
296
300
  - `nodes_by_job`: Similar to `nodes_by_machine`, but maps jobs to their operation nodes, useful for job-specific traversal.
297
301
 
298
- #### Agent-Task Graph
302
+ #### Resource-Task Graph
299
303
 
300
- Introduced in the paper "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning" by [Park et al. (2021)](https://arxiv.org/abs/2106.03051), the Agent-Task Graph is a graph that represents the scheduling problem as a multi-agent reinforcement learning problem.
304
+ Introduced in the paper "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning" by [Park et al. (2021)](https://arxiv.org/abs/2106.03051), the resource-task graph (orginally named "agent-task graph") is a graph that represents the scheduling problem as a multi-agent reinforcement learning problem.
301
305
 
302
306
  In contrast to the disjunctive graph, instead of connecting operations
303
307
  that share the same resources directly by disjunctive edges, operation
@@ -308,25 +312,27 @@ from the same job are connected by non-directed edges too.
308
312
 
309
313
  ```python
310
314
  from job_shop_lib.graphs import (
311
- build_complete_agent_task_graph,
312
- build_agent_task_graph_with_jobs,
313
- build_agent_task_graph,
315
+ build_complete_resource_task_graph,
316
+ build_resource_task_graph_with_jobs,
317
+ build_resource_task_graph,
314
318
  )
315
- from job_shop_lib.visualization import plot_agent_task_graph
319
+ from job_shop_lib.visualization import plot_resource_task_graph
316
320
 
317
- complete_agent_task_graph = build_complete_agent_task_graph(instance)
321
+ complete_resource_task_graph = build_complete_resource_task_graph(instance)
318
322
 
319
- fig = plot_agent_task_graph(complete_agent_task_graph)
323
+ fig = plot_resource_task_graph(complete_agent_task_graph)
320
324
  plt.show()
321
325
  ```
322
326
 
323
327
  <div align="center">
324
- <img src="docs/source/images/agent_task_graph.png" width="300">
328
+ <img src="docs/source/examples/output/agent_task_graph.png" width="300">
325
329
  </div>
326
330
  <br>
327
331
 
328
332
  ----
329
333
 
334
+ The library generalizes this graph by allowing the addition of job nodes and a global one (see `build_resource_task_graph_with_jobs` and `build_resource_task_graph`).
335
+
330
336
  For more details, check the [examples](examples) folder.
331
337
 
332
338
  ## Installation for development
@@ -12,7 +12,7 @@
12
12
 
13
13
  </div>
14
14
 
15
- JobShopLib is a Python package for creating, solving, and visualizing Job Shop Scheduling Problems (JSSP).
15
+ JobShopLib is a Python package for creating, solving, and visualizing job shop scheduling problems (JSSP).
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
 
@@ -33,10 +33,10 @@ pip install job-shop-lib
33
33
  See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcowwTZ4H?usp=sharing) Google Colab notebook for a quick start guide!
34
34
 
35
35
 
36
- Version 1.0.0 is currently in alpha stage and can be installed with:
36
+ Version 1.0.0 is currently in beta stage and can be installed with:
37
37
 
38
38
  ```bash
39
- pip install job-shop-lib==1.0.0a5
39
+ pip install job-shop-lib==1.0.0b2
40
40
  ```
41
41
 
42
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.
@@ -65,7 +65,11 @@ Although this version is not stable and may contain breaking changes in subseque
65
65
  - **Agent-Task Graphs**: Encode instances as agent-task graphs (introduced in [ScheduleNet paper](https://arxiv.org/abs/2106.03051)). See [Agent-Task Graph](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/07-Agent-Task-Graph.ipynb).
66
66
  - Build your own custom graphs with the `JobShopGraph` class.
67
67
 
68
+ <<<<<<< HEAD
69
+ - **Gymnasium Environments**: Two environments for solving the problem with graph neural networks (GNNs) or any other method, and reinforcement learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
70
+ =======
68
71
  - **Gymnasium Environments**: Two environments for solving the problem with Graph Neural Networks (GNNs) or any other method, and Reinforcement Learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
72
+ >>>>>>> 031bdf3 ([Docs] Update links in README to point to the correct GitHub URLs for tutorials and examples)
69
73
 
70
74
  <!-- end key features -->
71
75
 
@@ -221,7 +225,7 @@ The dashed red line represents the current time step, which is computed as the e
221
225
 
222
226
  ### Representing Instances as Graphs
223
227
 
224
- One of the main purposes of this library is to provide an easy way to encode instances as graphs. This can be very useful, not only for visualization purposes but also for developing Graph Neural Network-based algorithms.
228
+ One of the main purposes of this library is to provide an easy way to encode instances as graphs. This can be very useful, not only for visualization purposes but also for developing graph neural network-based algorithms.
225
229
 
226
230
  A graph is represented by the `JobShopGraph` class, which internally stores a `networkx.DiGraph` object.
227
231
 
@@ -229,7 +233,7 @@ A graph is represented by the `JobShopGraph` class, which internally stores a `n
229
233
 
230
234
  The disjunctive graph is created by first adding nodes representing each operation in the jobs, along with two special nodes: a source $S$ and a sink $T$. Each operation node is linked to the next operation in its job sequence by **conjunctive edges**, forming a path from the source to the sink. These edges represent the order in which operations of a single job must be performed.
231
235
 
232
- Additionally, the graph includes **disjunctive edges** between operations that use the same machine but belong to different jobs. These edges are bidirectional, indicating that either of the connected operations can be performed first. The disjunctive edges thus represent the scheduling choices available: the order in which operations sharing a machine can be processed. Solving the Job Shop Scheduling problem involves choosing a direction for each disjunctive edge such that the overall processing time is minimized.
236
+ Additionally, the graph includes **disjunctive edges** between operations that use the same machine but belong to different jobs. These edges are bidirectional, indicating that either of the connected operations can be performed first. The disjunctive edges thus represent the scheduling choices available: the order in which operations sharing a machine can be processed. Solving the job shop scheduling problem involves choosing a direction for each disjunctive edge such that the overall processing time is minimized.
233
237
 
234
238
  ```python
235
239
  from job_shop_lib.visualization import plot_disjunctive_graph
@@ -271,9 +275,9 @@ Other attributes include:
271
275
  - `nodes_by_machine`: A nested list mapping each machine to its associated operation nodes, aiding in machine-specific analysis.
272
276
  - `nodes_by_job`: Similar to `nodes_by_machine`, but maps jobs to their operation nodes, useful for job-specific traversal.
273
277
 
274
- #### Agent-Task Graph
278
+ #### Resource-Task Graph
275
279
 
276
- Introduced in the paper "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning" by [Park et al. (2021)](https://arxiv.org/abs/2106.03051), the Agent-Task Graph is a graph that represents the scheduling problem as a multi-agent reinforcement learning problem.
280
+ Introduced in the paper "ScheduleNet: Learn to solve multi-agent scheduling problems with reinforcement learning" by [Park et al. (2021)](https://arxiv.org/abs/2106.03051), the resource-task graph (orginally named "agent-task graph") is a graph that represents the scheduling problem as a multi-agent reinforcement learning problem.
277
281
 
278
282
  In contrast to the disjunctive graph, instead of connecting operations
279
283
  that share the same resources directly by disjunctive edges, operation
@@ -284,25 +288,27 @@ from the same job are connected by non-directed edges too.
284
288
 
285
289
  ```python
286
290
  from job_shop_lib.graphs import (
287
- build_complete_agent_task_graph,
288
- build_agent_task_graph_with_jobs,
289
- build_agent_task_graph,
291
+ build_complete_resource_task_graph,
292
+ build_resource_task_graph_with_jobs,
293
+ build_resource_task_graph,
290
294
  )
291
- from job_shop_lib.visualization import plot_agent_task_graph
295
+ from job_shop_lib.visualization import plot_resource_task_graph
292
296
 
293
- complete_agent_task_graph = build_complete_agent_task_graph(instance)
297
+ complete_resource_task_graph = build_complete_resource_task_graph(instance)
294
298
 
295
- fig = plot_agent_task_graph(complete_agent_task_graph)
299
+ fig = plot_resource_task_graph(complete_agent_task_graph)
296
300
  plt.show()
297
301
  ```
298
302
 
299
303
  <div align="center">
300
- <img src="docs/source/images/agent_task_graph.png" width="300">
304
+ <img src="docs/source/examples/output/agent_task_graph.png" width="300">
301
305
  </div>
302
306
  <br>
303
307
 
304
308
  ----
305
309
 
310
+ The library generalizes this graph by allowing the addition of job nodes and a global one (see `build_resource_task_graph_with_jobs` and `build_resource_task_graph`).
311
+
306
312
  For more details, check the [examples](examples) folder.
307
313
 
308
314
  ## Installation for development
@@ -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-a.5"
22
+ __version__ = "1.0.0-b.2"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import functools
7
- from typing import Any
7
+ from typing import Any, List, Union, Dict
8
8
 
9
9
  import numpy as np
10
10
  from numpy.typing import NDArray
@@ -51,14 +51,14 @@ class JobShopInstance:
51
51
  total_duration
52
52
 
53
53
  Attributes:
54
- jobs (list[list[Operation]]):
54
+ jobs (List[List[Operation]]):
55
55
  A list of lists of operations. Each list of operations represents
56
56
  a job, and the operations are ordered by their position in the job.
57
57
  The ``job_id``, ``position_in_job``, and ``operation_id``
58
58
  attributes of the operations are set when the instance is created.
59
59
  name (str):
60
60
  A string with the name of the instance.
61
- metadata (dict[str, Any]):
61
+ metadata (Dict[str, Any]):
62
62
  A dictionary with additional information about the instance.
63
63
 
64
64
  Args:
@@ -81,16 +81,16 @@ class JobShopInstance:
81
81
 
82
82
  def __init__(
83
83
  self,
84
- jobs: list[list[Operation]],
84
+ jobs: List[List[Operation]],
85
85
  name: str = "JobShopInstance",
86
86
  set_operation_attributes: bool = True,
87
87
  **metadata: Any,
88
88
  ):
89
- self.jobs: list[list[Operation]] = jobs
89
+ self.jobs: List[List[Operation]] = jobs
90
90
  if set_operation_attributes:
91
91
  self.set_operation_attributes()
92
92
  self.name: str = name
93
- self.metadata: dict[str, Any] = metadata
93
+ self.metadata: Dict[str, Any] = metadata
94
94
 
95
95
  def set_operation_attributes(self):
96
96
  """Sets the ``job_id``, ``position_in_job``, and ``operation_id``
@@ -125,10 +125,10 @@ class JobShopInstance:
125
125
  @classmethod
126
126
  def from_taillard_file(
127
127
  cls,
128
- file_path: os.PathLike | str | bytes,
128
+ file_path: Union[os.PathLike, str, bytes],
129
129
  encoding: str = "utf-8",
130
130
  comment_symbol: str = "#",
131
- name: str | None = None,
131
+ name: Union[str, None] = None,
132
132
  **metadata: Any,
133
133
  ) -> JobShopInstance:
134
134
  """Creates a JobShopInstance from a file following Taillard's format.
@@ -178,7 +178,7 @@ class JobShopInstance:
178
178
  name = name.split(".")[0]
179
179
  return cls(jobs=jobs, name=name, **metadata)
180
180
 
181
- def to_dict(self) -> dict[str, Any]:
181
+ def to_dict(self) -> Dict[str, Any]:
182
182
  """Returns a dictionary representation of the instance.
183
183
 
184
184
  This representation is useful for saving the instance to a JSON file,
@@ -186,7 +186,7 @@ class JobShopInstance:
186
186
  like Taillard's.
187
187
 
188
188
  Returns:
189
- dict[str, Any]: The returned dictionary has the following
189
+ Dict[str, Any]: The returned dictionary has the following
190
190
  structure:
191
191
 
192
192
  .. code-block:: python
@@ -208,10 +208,10 @@ class JobShopInstance:
208
208
  @classmethod
209
209
  def from_matrices(
210
210
  cls,
211
- duration_matrix: list[list[int]],
212
- machines_matrix: list[list[list[int]]] | list[list[int]],
211
+ duration_matrix: List[List[int]],
212
+ machines_matrix: List[List[List[int]]] | List[List[int]],
213
213
  name: str = "JobShopInstance",
214
- metadata: dict[str, Any] | None = None,
214
+ metadata: Dict[str, Any] | None = None,
215
215
  ) -> JobShopInstance:
216
216
  """Creates a :class:`JobShopInstance` from duration and machines
217
217
  matrices.
@@ -233,7 +233,7 @@ class JobShopInstance:
233
233
  Returns:
234
234
  A :class:`JobShopInstance` object.
235
235
  """
236
- jobs: list[list[Operation]] = [[] for _ in range(len(duration_matrix))]
236
+ jobs: List[List[Operation]] = [[] for _ in range(len(duration_matrix))]
237
237
 
238
238
  num_jobs = len(duration_matrix)
239
239
  for job_id in range(num_jobs):
@@ -290,7 +290,7 @@ class JobShopInstance:
290
290
  )
291
291
 
292
292
  @functools.cached_property
293
- def durations_matrix(self) -> list[list[int]]:
293
+ def durations_matrix(self) -> List[List[int]]:
294
294
  """Returns the duration matrix of the instance.
295
295
 
296
296
  The duration of the operation with ``job_id`` i and ``position_in_job``
@@ -305,7 +305,7 @@ class JobShopInstance:
305
305
  return [[operation.duration for operation in job] for job in self.jobs]
306
306
 
307
307
  @functools.cached_property
308
- def machines_matrix(self) -> list[list[list[int]]] | list[list[int]]:
308
+ def machines_matrix(self) -> Union[List[List[List[int]]], List[List[int]]]:
309
309
  """Returns the machines matrix of the instance.
310
310
 
311
311
  If the instance is flexible (i.e., if any operation has more than one
@@ -342,7 +342,7 @@ class JobShopInstance:
342
342
  >>> jobs = [[Operation(0, 2), Operation(1, 3)], [Operation(0, 4)]]
343
343
  >>> instance = JobShopInstance(jobs)
344
344
  >>> instance.durations_matrix_array
345
- array([[ 2., 2.],
345
+ array([[ 2., 3.],
346
346
  [ 4., nan]], dtype=float32)
347
347
  """
348
348
  duration_matrix = self.durations_matrix
@@ -358,8 +358,7 @@ class JobShopInstance:
358
358
 
359
359
  Example:
360
360
  >>> jobs = [
361
- ... [Operation(machines=[0, 1], 2), Operation(machines=1, 3)],
362
- ... [Operation(machines=0, 6)],
361
+ ... [Operation([0, 1], 2), Operation(1, 3)], [Operation(0, 6)]
363
362
  ... ]
364
363
  >>> instance = JobShopInstance(jobs)
365
364
  >>> instance.machines_matrix_array
@@ -372,25 +371,25 @@ class JobShopInstance:
372
371
  machines_matrix = self.machines_matrix
373
372
  if self.is_flexible:
374
373
  # False positive from mypy, the type of machines_matrix is
375
- # list[list[list[int]]] here
374
+ # List[List[List[int]]] here
376
375
  return self._fill_matrix_with_nans_3d(
377
376
  machines_matrix # type: ignore[arg-type]
378
377
  )
379
378
 
380
379
  # False positive from mypy, the type of machines_matrix is
381
- # list[list[int]] here
380
+ # List[List[int]] here
382
381
  return self._fill_matrix_with_nans_2d(
383
382
  machines_matrix # type: ignore[arg-type]
384
383
  )
385
384
 
386
385
  @functools.cached_property
387
- def operations_by_machine(self) -> list[list[Operation]]:
386
+ def operations_by_machine(self) -> List[List[Operation]]:
388
387
  """Returns a list of lists of operations.
389
388
 
390
389
  The i-th list contains the operations that can be processed in the
391
390
  machine with id i.
392
391
  """
393
- operations_by_machine: list[list[Operation]] = [
392
+ operations_by_machine: List[List[Operation]] = [
394
393
  [] for _ in range(self.num_machines)
395
394
  ]
396
395
  for job in self.jobs:
@@ -410,7 +409,7 @@ class JobShopInstance:
410
409
  )
411
410
 
412
411
  @functools.cached_property
413
- def max_duration_per_job(self) -> list[float]:
412
+ def max_duration_per_job(self) -> List[float]:
414
413
  """Returns the maximum duration of each job in the instance.
415
414
 
416
415
  The maximum duration of the job with id i is stored in the i-th
@@ -421,7 +420,7 @@ class JobShopInstance:
421
420
  return [max(op.duration for op in job) for job in self.jobs]
422
421
 
423
422
  @functools.cached_property
424
- def max_duration_per_machine(self) -> list[int]:
423
+ def max_duration_per_machine(self) -> List[int]:
425
424
  """Returns the maximum duration of each machine in the instance.
426
425
 
427
426
  The maximum duration of the machine with id i is stored in the i-th
@@ -440,7 +439,7 @@ class JobShopInstance:
440
439
  return max_duration_per_machine
441
440
 
442
441
  @functools.cached_property
443
- def job_durations(self) -> list[int]:
442
+ def job_durations(self) -> List[int]:
444
443
  """Returns a list with the duration of each job in the instance.
445
444
 
446
445
  The duration of a job is the sum of the durations of its operations.
@@ -451,7 +450,7 @@ class JobShopInstance:
451
450
  return [sum(op.duration for op in job) for job in self.jobs]
452
451
 
453
452
  @functools.cached_property
454
- def machine_loads(self) -> list[int]:
453
+ def machine_loads(self) -> List[int]:
455
454
  """Returns the total machine load of each machine in the instance.
456
455
 
457
456
  The total machine load of a machine is the sum of the durations of the
@@ -475,7 +474,7 @@ class JobShopInstance:
475
474
 
476
475
  @staticmethod
477
476
  def _fill_matrix_with_nans_2d(
478
- matrix: list[list[int]],
477
+ matrix: List[List[int]],
479
478
  ) -> NDArray[np.float32]:
480
479
  """Fills a matrix with ``np.nan`` values.
481
480
 
@@ -497,7 +496,7 @@ class JobShopInstance:
497
496
 
498
497
  @staticmethod
499
498
  def _fill_matrix_with_nans_3d(
500
- matrix: list[list[list[int]]],
499
+ matrix: List[List[List[int]]],
501
500
  ) -> NDArray[np.float32]:
502
501
  """Fills a 3D matrix with ``np.nan`` values.
503
502
 
@@ -523,3 +522,9 @@ class JobShopInstance:
523
522
  for j, inner_row in enumerate(row):
524
523
  squared_matrix[i, j, : len(inner_row)] = inner_row
525
524
  return squared_matrix
525
+
526
+
527
+ if __name__ == "__main__":
528
+ import doctest
529
+
530
+ doctest.testmod()
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from typing import Union, List
6
+
5
7
  from job_shop_lib.exceptions import UninitializedAttributeError
6
8
 
7
9
 
@@ -59,8 +61,8 @@ class Operation:
59
61
  ),
60
62
  }
61
63
 
62
- def __init__(self, machines: int | list[int], duration: int):
63
- self.machines: list[int] = (
64
+ def __init__(self, machines: Union[int, List[int]], duration: int):
65
+ self.machines: List[int] = (
64
66
  [machines] if isinstance(machines, int) else machines
65
67
  )
66
68
  self.duration: int = duration
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any
5
+ from typing import Any, List, Union, Dict, Optional
6
6
  from collections import deque
7
7
 
8
8
  from job_shop_lib import ScheduledOperation, JobShopInstance
@@ -55,7 +55,7 @@ class Schedule:
55
55
  def __init__(
56
56
  self,
57
57
  instance: JobShopInstance,
58
- schedule: list[list[ScheduledOperation]] | None = None,
58
+ schedule: Optional[List[List[ScheduledOperation]]] = None,
59
59
  **metadata: Any,
60
60
  ):
61
61
  if schedule is None:
@@ -65,19 +65,19 @@ class Schedule:
65
65
 
66
66
  self.instance: JobShopInstance = instance
67
67
  self._schedule = schedule
68
- self.metadata: dict[str, Any] = metadata
68
+ self.metadata: Dict[str, Any] = metadata
69
69
 
70
70
  def __repr__(self) -> str:
71
71
  return str(self.schedule)
72
72
 
73
73
  @property
74
- def schedule(self) -> list[list[ScheduledOperation]]:
74
+ def schedule(self) -> List[List[ScheduledOperation]]:
75
75
  """A list of lists of :class:`ScheduledOperation` objects. Each list
76
76
  represents the order of operations on a machine."""
77
77
  return self._schedule
78
78
 
79
79
  @schedule.setter
80
- def schedule(self, new_schedule: list[list[ScheduledOperation]]):
80
+ def schedule(self, new_schedule: List[List[ScheduledOperation]]):
81
81
  Schedule.check_schedule(new_schedule)
82
82
  self._schedule = new_schedule
83
83
 
@@ -103,7 +103,7 @@ class Schedule:
103
103
  - **"metadata"**: A dictionary with additional information
104
104
  about the schedule.
105
105
  """
106
- job_sequences: list[list[int]] = []
106
+ job_sequences: List[List[int]] = []
107
107
  for machine_schedule in self.schedule:
108
108
  job_sequences.append(
109
109
  [operation.job_id for operation in machine_schedule]
@@ -117,9 +117,9 @@ class Schedule:
117
117
 
118
118
  @staticmethod
119
119
  def from_dict(
120
- instance: dict[str, Any] | JobShopInstance,
121
- job_sequences: list[list[int]],
122
- metadata: dict[str, Any] | None = None,
120
+ instance: Union[Dict[str, Any], JobShopInstance],
121
+ job_sequences: List[List[int]],
122
+ metadata: Optional[Dict[str, Any]] = None,
123
123
  ) -> Schedule:
124
124
  """Creates a schedule from a dictionary representation."""
125
125
  if isinstance(instance, dict):
@@ -131,7 +131,7 @@ class Schedule:
131
131
  @staticmethod
132
132
  def from_job_sequences(
133
133
  instance: JobShopInstance,
134
- job_sequences: list[list[int]],
134
+ job_sequences: List[List[int]],
135
135
  ) -> Schedule:
136
136
  """Creates an active schedule from a list of job sequences.
137
137
 
@@ -240,7 +240,7 @@ class Schedule:
240
240
  return previous_operation.end_time <= scheduled_operation.start_time
241
241
 
242
242
  @staticmethod
243
- def check_schedule(schedule: list[list[ScheduledOperation]]):
243
+ def check_schedule(schedule: List[List[ScheduledOperation]]):
244
244
  """Checks if a schedule is valid and raises a
245
245
  :class:`~exceptions.ValidationError` if it is not.
246
246
 
@@ -1,6 +1,6 @@
1
1
  """Contains functions to load benchmark instances from a JSON file."""
2
2
 
3
- from typing import Any
3
+ from typing import Any, Dict
4
4
 
5
5
  import functools
6
6
  import json
@@ -10,7 +10,7 @@ from job_shop_lib import JobShopInstance
10
10
 
11
11
 
12
12
  @functools.cache
13
- def load_all_benchmark_instances() -> dict[str, JobShopInstance]:
13
+ def load_all_benchmark_instances() -> Dict[str, JobShopInstance]:
14
14
  """Loads all benchmark instances available.
15
15
 
16
16
  Returns:
@@ -48,7 +48,7 @@ def load_benchmark_instance(name: str) -> JobShopInstance:
48
48
 
49
49
 
50
50
  @functools.cache
51
- def load_benchmark_json() -> dict[str, dict[str, Any]]:
51
+ def load_benchmark_json() -> Dict[str, Dict[str, Any]]:
52
52
  """Loads the raw JSON file containing the benchmark instances.
53
53
 
54
54
  Results are cached to avoid reading the file multiple times.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any
5
+ from typing import Any, Dict, List, Tuple
6
6
  import time
7
7
 
8
8
  from ortools.sat.python import cp_model
@@ -62,7 +62,7 @@ class ORToolsSolver(BaseSolver):
62
62
  self._makespan: cp_model.IntVar | None = None
63
63
  self.model = cp_model.CpModel()
64
64
  self.solver = cp_model.CpSolver()
65
- self._operations_start: dict[Operation, tuple[IntVar, IntVar]] = {}
65
+ self._operations_start: Dict[Operation, Tuple[IntVar, IntVar]] = {}
66
66
 
67
67
  def __call__(self, instance: JobShopInstance) -> Schedule:
68
68
  """Equivalent to calling the :meth:`~ORToolsSolver.solve` method.
@@ -152,15 +152,15 @@ class ORToolsSolver(BaseSolver):
152
152
  self._set_objective(instance)
153
153
 
154
154
  def _create_schedule(
155
- self, instance: JobShopInstance, metadata: dict[str, Any]
155
+ self, instance: JobShopInstance, metadata: Dict[str, Any]
156
156
  ) -> Schedule:
157
157
  """Creates a Schedule object from the solution."""
158
- operations_start: dict[Operation, int] = {
158
+ operations_start: Dict[Operation, int] = {
159
159
  operation: self.solver.Value(start_var)
160
160
  for operation, (start_var, _) in self._operations_start.items()
161
161
  }
162
162
 
163
- unsorted_schedule: list[list[ScheduledOperation]] = [
163
+ unsorted_schedule: List[List[ScheduledOperation]] = [
164
164
  [] for _ in range(instance.num_machines)
165
165
  ]
166
166
  for operation, start_time in operations_start.items():
@@ -235,7 +235,7 @@ class ORToolsSolver(BaseSolver):
235
235
  each machine."""
236
236
 
237
237
  # Create interval variables for each operation on each machine
238
- machines_operations: list[list[tuple[tuple[IntVar, IntVar], int]]] = [
238
+ machines_operations: List[List[Tuple[Tuple[IntVar, IntVar], int]]] = [
239
239
  [] for _ in range(instance.num_machines)
240
240
  ]
241
241
  for job in instance.jobs:
@@ -8,6 +8,7 @@ Problem step-by-step.
8
8
  DispatcherObserver
9
9
  HistoryObserver
10
10
  UnscheduledOperationsObserver
11
+ OptimalOperationsObserver
11
12
  ReadyOperationsFilter
12
13
  DispatcherObserverConfig
13
14
  filter_dominated_operations
@@ -25,9 +26,8 @@ from ._dispatcher import Dispatcher, DispatcherObserver
25
26
  from ._history_observer import (
26
27
  HistoryObserver,
27
28
  )
28
- from ._unscheduled_operations_observer import (
29
- UnscheduledOperationsObserver,
30
- )
29
+ from ._unscheduled_operations_observer import UnscheduledOperationsObserver
30
+ from ._optimal_operations_observer import OptimalOperationsObserver
31
31
  from ._ready_operation_filters import (
32
32
  filter_dominated_operations,
33
33
  filter_non_immediate_machines,
@@ -57,4 +57,5 @@ __all__ = [
57
57
  "ReadyOperationsFilter",
58
58
  "filter_non_idle_machines",
59
59
  "filter_non_immediate_operations",
60
+ "OptimalOperationsObserver",
60
61
  ]