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.
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/PKG-INFO +21 -15
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/README.md +20 -14
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/__init__.py +1 -1
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_job_shop_instance.py +34 -29
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_operation.py +4 -2
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_schedule.py +11 -11
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/benchmarking/_load_benchmark.py +3 -3
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/constraint_programming/_ortools_solver.py +6 -6
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/__init__.py +4 -3
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_dispatcher.py +19 -19
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_dispatcher_observer_config.py +4 -4
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_factories.py +4 -2
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_history_observer.py +2 -1
- job_shop_lib-1.0.0b2/job_shop_lib/dispatching/_optimal_operations_observer.py +115 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_ready_operation_filters.py +19 -18
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/_unscheduled_operations_observer.py +4 -3
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +7 -8
- {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
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_factory.py +13 -14
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_feature_observer.py +9 -8
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +2 -1
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +4 -2
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/__init__.py +37 -1
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +4 -2
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +50 -20
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -8
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_machine_chooser_factory.py +4 -3
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/rules/_utils.py +9 -8
- job_shop_lib-1.0.0b2/job_shop_lib/generation/__init__.py +19 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/generation/_general_instance_generator.py +42 -64
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/generation/_instance_generator.py +11 -7
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/generation/_transformations.py +5 -4
- job_shop_lib-1.0.0b2/job_shop_lib/generation/_utils.py +124 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/__init__.py +7 -7
- 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
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_job_shop_graph.py +17 -13
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_node.py +6 -4
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +4 -2
- {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
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_reward_observers.py +3 -1
- {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
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_types_and_constants.py +1 -1
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/reinforcement_learning/_utils.py +3 -3
- job_shop_lib-1.0.0b2/job_shop_lib/visualization/__init__.py +0 -0
- {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
- {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
- {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
- {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
- job_shop_lib-1.0.0b2/job_shop_lib/visualization/graphs/__init__.py +29 -0
- {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
- job_shop_lib-1.0.0b2/job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +389 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/pyproject.toml +3 -1
- job_shop_lib-1.0.0a5/job_shop_lib/generation/__init__.py +0 -11
- job_shop_lib-1.0.0a5/job_shop_lib/visualization/_plot_agent_task_graph.py +0 -276
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/LICENSE +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_base_solver.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/_scheduled_operation.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/benchmarking/__init__.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/benchmarking/benchmark_instances.json +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/constraint_programming/__init__.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/__init__.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_duration_observer.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py +0 -0
- {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
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/exceptions.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_build_disjunctive_graph.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/_constants.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/__init__.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/_graph_updater.py +0 -0
- {job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/graphs/graph_updaters/_utils.py +0 -0
- {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.
|
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
|
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
|
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.
|
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
|
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
|
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
|
-
####
|
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
|
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
|
-
|
312
|
-
|
313
|
-
|
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
|
319
|
+
from job_shop_lib.visualization import plot_resource_task_graph
|
316
320
|
|
317
|
-
|
321
|
+
complete_resource_task_graph = build_complete_resource_task_graph(instance)
|
318
322
|
|
319
|
-
fig =
|
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/
|
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
|
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
|
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.
|
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
|
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
|
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
|
-
####
|
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
|
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
|
-
|
288
|
-
|
289
|
-
|
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
|
295
|
+
from job_shop_lib.visualization import plot_resource_task_graph
|
292
296
|
|
293
|
-
|
297
|
+
complete_resource_task_graph = build_complete_resource_task_graph(instance)
|
294
298
|
|
295
|
-
fig =
|
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/
|
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
|
@@ -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 (
|
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 (
|
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:
|
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:
|
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:
|
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
|
128
|
+
file_path: Union[os.PathLike, str, bytes],
|
129
129
|
encoding: str = "utf-8",
|
130
130
|
comment_symbol: str = "#",
|
131
|
-
name: str
|
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) ->
|
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
|
-
|
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:
|
212
|
-
machines_matrix:
|
211
|
+
duration_matrix: List[List[int]],
|
212
|
+
machines_matrix: List[List[List[int]]] | List[List[int]],
|
213
213
|
name: str = "JobShopInstance",
|
214
|
-
metadata:
|
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:
|
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) ->
|
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) ->
|
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.,
|
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(
|
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
|
-
#
|
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
|
-
#
|
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) ->
|
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:
|
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) ->
|
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) ->
|
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) ->
|
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) ->
|
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:
|
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:
|
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
|
63
|
-
self.machines:
|
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:
|
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:
|
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) ->
|
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:
|
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:
|
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:
|
121
|
-
job_sequences:
|
122
|
-
metadata:
|
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:
|
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:
|
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() ->
|
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() ->
|
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.
|
{job_shop_lib-1.0.0a5 → job_shop_lib-1.0.0b2}/job_shop_lib/constraint_programming/_ortools_solver.py
RENAMED
@@ -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:
|
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:
|
155
|
+
self, instance: JobShopInstance, metadata: Dict[str, Any]
|
156
156
|
) -> Schedule:
|
157
157
|
"""Creates a Schedule object from the solution."""
|
158
|
-
operations_start:
|
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:
|
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:
|
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
|
-
|
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
|
]
|