job-shop-lib 1.0.0a5__py3-none-any.whl → 1.0.0b2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. job_shop_lib/__init__.py +1 -1
  2. job_shop_lib/_job_shop_instance.py +34 -29
  3. job_shop_lib/_operation.py +4 -2
  4. job_shop_lib/_schedule.py +11 -11
  5. job_shop_lib/benchmarking/_load_benchmark.py +3 -3
  6. job_shop_lib/constraint_programming/_ortools_solver.py +6 -6
  7. job_shop_lib/dispatching/__init__.py +4 -3
  8. job_shop_lib/dispatching/_dispatcher.py +19 -19
  9. job_shop_lib/dispatching/_dispatcher_observer_config.py +4 -4
  10. job_shop_lib/dispatching/_factories.py +4 -2
  11. job_shop_lib/dispatching/_history_observer.py +2 -1
  12. job_shop_lib/dispatching/_optimal_operations_observer.py +115 -0
  13. job_shop_lib/dispatching/_ready_operation_filters.py +19 -18
  14. job_shop_lib/dispatching/_unscheduled_operations_observer.py +4 -3
  15. job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +7 -8
  16. job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py +3 -1
  17. job_shop_lib/dispatching/feature_observers/_factory.py +13 -14
  18. job_shop_lib/dispatching/feature_observers/_feature_observer.py +9 -8
  19. job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +2 -1
  20. job_shop_lib/dispatching/feature_observers/_is_ready_observer.py +4 -2
  21. job_shop_lib/dispatching/rules/__init__.py +37 -1
  22. job_shop_lib/dispatching/rules/_dispatching_rule_factory.py +4 -2
  23. job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +50 -20
  24. job_shop_lib/dispatching/rules/_dispatching_rules_functions.py +9 -8
  25. job_shop_lib/dispatching/rules/_machine_chooser_factory.py +4 -3
  26. job_shop_lib/dispatching/rules/_utils.py +9 -8
  27. job_shop_lib/generation/__init__.py +8 -0
  28. job_shop_lib/generation/_general_instance_generator.py +42 -64
  29. job_shop_lib/generation/_instance_generator.py +11 -7
  30. job_shop_lib/generation/_transformations.py +5 -4
  31. job_shop_lib/generation/_utils.py +124 -0
  32. job_shop_lib/graphs/__init__.py +7 -7
  33. job_shop_lib/graphs/{_build_agent_task_graph.py → _build_resource_task_graphs.py} +26 -24
  34. job_shop_lib/graphs/_job_shop_graph.py +17 -13
  35. job_shop_lib/graphs/_node.py +6 -4
  36. job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +4 -2
  37. job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +40 -20
  38. job_shop_lib/reinforcement_learning/_reward_observers.py +3 -1
  39. job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +89 -22
  40. job_shop_lib/reinforcement_learning/_types_and_constants.py +1 -1
  41. job_shop_lib/reinforcement_learning/_utils.py +3 -3
  42. job_shop_lib/visualization/__init__.py +0 -60
  43. job_shop_lib/visualization/gantt/__init__.py +48 -0
  44. job_shop_lib/visualization/{_gantt_chart_creator.py → gantt/_gantt_chart_creator.py} +12 -12
  45. job_shop_lib/visualization/{_gantt_chart_video_and_gif_creation.py → gantt/_gantt_chart_video_and_gif_creation.py} +22 -22
  46. job_shop_lib/visualization/{_plot_gantt_chart.py → gantt/_plot_gantt_chart.py} +12 -13
  47. job_shop_lib/visualization/graphs/__init__.py +29 -0
  48. job_shop_lib/visualization/{_plot_disjunctive_graph.py → graphs/_plot_disjunctive_graph.py} +18 -16
  49. job_shop_lib/visualization/graphs/_plot_resource_task_graph.py +389 -0
  50. {job_shop_lib-1.0.0a5.dist-info → job_shop_lib-1.0.0b2.dist-info}/METADATA +21 -15
  51. job_shop_lib-1.0.0b2.dist-info/RECORD +70 -0
  52. job_shop_lib/visualization/_plot_agent_task_graph.py +0 -276
  53. job_shop_lib-1.0.0a5.dist-info/RECORD +0 -66
  54. {job_shop_lib-1.0.0a5.dist-info → job_shop_lib-1.0.0b2.dist-info}/LICENSE +0 -0
  55. {job_shop_lib-1.0.0a5.dist-info → job_shop_lib-1.0.0b2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,389 @@
1
+ """Contains functions to plot a resource-task graph representation of a job
2
+ shop instance.
3
+
4
+ It was introduced by Junyoung Park et al. (2021).
5
+ In contrast to the disjunctive graph, instead of connecting operations that
6
+ share the same resources directly by disjunctive edges, operation nodes are
7
+ connected with machine ones. All machine nodes are connected between them, and
8
+ all operation nodes from the same job are connected by non-directed edges too.
9
+ """
10
+
11
+ from collections.abc import Callable
12
+ from copy import deepcopy
13
+ from typing import Optional, Any, Tuple, Dict, Union, List
14
+
15
+ import matplotlib.pyplot as plt
16
+ import matplotlib.colors as mcolors
17
+ import networkx as nx
18
+
19
+ from job_shop_lib.graphs import NodeType, JobShopGraph, Node
20
+
21
+
22
+ def plot_resource_task_graph(
23
+ job_shop_graph: JobShopGraph,
24
+ *,
25
+ title: Optional[str] = None,
26
+ figsize: Tuple[int, int] = (10, 10),
27
+ layout: Optional[Dict[Node, Tuple[float, float]]] = None,
28
+ node_size: int = 1200,
29
+ node_font_color: str = "k",
30
+ font_size: int = 10,
31
+ alpha: float = 0.95,
32
+ add_legend: bool = False,
33
+ node_shapes: Optional[Dict[str, str]] = None,
34
+ node_color_map: Optional[
35
+ Callable[[Node], Tuple[float, float, float, float]]
36
+ ] = None,
37
+ default_node_color: Union[
38
+ str, Tuple[float, float, float, float]
39
+ ] = "lightblue",
40
+ machine_color_map_name: str = "tab10",
41
+ legend_text: str = "$p_{ij}$ = duration of $O_{ij}$",
42
+ edge_additional_params: Optional[Dict[str, Any]] = None,
43
+ draw_only_one_edge: bool = False,
44
+ ) -> plt.Figure:
45
+ """Returns a plot of the hetereogeneous graph of the instance.
46
+
47
+ Machine and job nodes are represented by squares, and the operation nodes
48
+ are represented by circles.
49
+
50
+ Args:
51
+ job_shop_graph:
52
+ The job shop graph instance.
53
+ title:
54
+ The title of the plot. If ``None``, the title "Heterogeneous Graph
55
+ Visualization: {instance_name}" is used. The default is ``None``.
56
+ figsize:
57
+ The size of the figure. It should be a tuple with the width and
58
+ height in inches. The default is ``(10, 10)``.
59
+ layout:
60
+ A dictionary with the position of each node in the graph. The keys
61
+ are the node ids, and the values are tuples with the x and y
62
+ coordinates. If ``None``, the :func:`three_columns_layout` function
63
+ is used. The default is ``None``.
64
+ node_size:
65
+ The size of the nodes. The default is 1000.
66
+ alpha:
67
+ The transparency of the nodes. It should be a float between 0 and
68
+ 1. The default is 0.95.
69
+ add_legend:
70
+ Whether to add a legend with the meaning of the colors and shapes.
71
+ The default is ``False``.
72
+ node_shapes:
73
+ A dictionary with the shapes of the nodes. The keys are the node
74
+ types, and the values are the shapes. The default is
75
+ ``{"machine": "s", "job": "d", "operation": "o", "global": "o"}``.
76
+ node_color_map:
77
+ A function that receives a node and returns a tuple with the RGBA
78
+ values of the color to use in the plot. If ``None``,
79
+ :func:`color_nodes_by_machine` is used.
80
+ machine_color_map_name:
81
+ The name of the colormap to use for the machines. This argument is
82
+ only used if ``node_color_map`` is ``None``. The default is
83
+ ``"tab10"``.
84
+
85
+ Returns:
86
+ The figure of the plot. This figure can be used to save the plot to a
87
+ file or to show it in a Jupyter notebook.
88
+ """
89
+ if title is None:
90
+ title = (
91
+ "Heterogeneous Graph Visualization:"
92
+ f"{job_shop_graph.instance.name}"
93
+ )
94
+ # Create a new figure and axis
95
+ fig, ax = plt.subplots(figsize=figsize)
96
+ fig.suptitle(title)
97
+
98
+ # Create the networkx graph
99
+ graph = job_shop_graph.graph
100
+ nodes = job_shop_graph.non_removed_nodes()
101
+
102
+ # Create the layout if it was not provided
103
+ if layout is None:
104
+ layout = three_columns_layout(job_shop_graph)
105
+
106
+ # Define colors and shapes
107
+ color_map = plt.get_cmap(machine_color_map_name)
108
+ machine_colors = {
109
+ machine.machine_id: color_map(i)
110
+ for i, machine in enumerate(
111
+ job_shop_graph.nodes_by_type[NodeType.MACHINE]
112
+ )
113
+ }
114
+ node_color_map = (
115
+ color_nodes_by_machine(machine_colors, default_node_color)
116
+ if node_color_map is None
117
+ else node_color_map
118
+ )
119
+ node_colors = [
120
+ node_color_map(node) for node in job_shop_graph.nodes
121
+ ] # We need to get the color of all nodes to avoid an index error
122
+ if node_shapes is None:
123
+ node_shapes = {
124
+ "machine": "s",
125
+ "job": "d",
126
+ "operation": "o",
127
+ "global": "o",
128
+ }
129
+
130
+ # Draw nodes with different shapes based on their type
131
+ for node_type, shape in node_shapes.items():
132
+ current_nodes = [
133
+ node.node_id
134
+ for node in nodes
135
+ if node.node_type.name.lower() == node_type
136
+ ]
137
+ nx.draw_networkx_nodes(
138
+ graph,
139
+ layout,
140
+ nodelist=current_nodes,
141
+ node_color=[node_colors[i] for i in current_nodes],
142
+ node_shape=shape,
143
+ ax=ax,
144
+ node_size=node_size,
145
+ alpha=alpha,
146
+ )
147
+
148
+ # Draw edges
149
+ if edge_additional_params is None:
150
+ edge_additional_params = {}
151
+ if draw_only_one_edge:
152
+ graph = deepcopy(graph)
153
+ edges = list(graph.edges)
154
+ present_edges = set()
155
+ for edge in edges:
156
+ unorder_edge = frozenset(edge)
157
+ if unorder_edge in present_edges:
158
+ graph.remove_edge(*edge)
159
+ else:
160
+ present_edges.add(unorder_edge)
161
+
162
+ nx.draw_networkx_edges(graph, layout, ax=ax, **edge_additional_params)
163
+
164
+ node_color_map = (
165
+ color_nodes_by_machine(machine_colors, "lightblue")
166
+ if node_color_map is None
167
+ else node_color_map
168
+ )
169
+ node_labels = {node.node_id: _get_node_label(node) for node in nodes}
170
+ nx.draw_networkx_labels(
171
+ graph,
172
+ layout,
173
+ node_labels,
174
+ ax=ax,
175
+ font_size=font_size,
176
+ font_color=node_font_color,
177
+ )
178
+
179
+ ax.set_axis_off()
180
+
181
+ plt.tight_layout()
182
+
183
+ # Add to the legend the meaning of m and d
184
+ if add_legend:
185
+ plt.figtext(0, 0.95, legend_text, wrap=True, fontsize=12)
186
+ return fig
187
+
188
+
189
+ def _get_node_label(node: Node) -> str:
190
+ if node.node_type == NodeType.OPERATION:
191
+ i = node.operation.job_id
192
+ j = node.operation.position_in_job
193
+ ij = str(i) + str(j)
194
+ return f"$p_{{{ij}}}={node.operation.duration}$"
195
+ if node.node_type == NodeType.MACHINE:
196
+ return f"$M_{node.machine_id}$"
197
+ if node.node_type == NodeType.JOB:
198
+ return f"$J_{node.job_id}$"
199
+ if node.node_type == NodeType.GLOBAL:
200
+ return "$G$"
201
+
202
+ raise ValueError(f"Invalid node type: {node.node_type}")
203
+
204
+
205
+ def _color_to_rgba(
206
+ color: Union[str, Tuple[float, float, float, float]]
207
+ ) -> Tuple[float, float, float, float]:
208
+ if isinstance(color, str):
209
+ return mcolors.to_rgba(color)
210
+ return color
211
+
212
+
213
+ def color_nodes_by_machine(
214
+ machine_colors: Dict[int, Tuple[float, float, float, float]],
215
+ default_color: Union[str, Tuple[float, float, float, float]],
216
+ ) -> Callable[[Node], Tuple[float, float, float, float]]:
217
+ """Returns a function that assigns a color to a node based on its type.
218
+
219
+ The function returns a color based on the node type. If the node is an
220
+ operation, the color is based on the machine it is assigned to. If the node
221
+ is a machine, the color is based on the machine id. If the node is a job or
222
+ global node, the color is the default color.
223
+
224
+ Args:
225
+ machine_colors:
226
+ A dictionary with the colors of each machine. The keys are the
227
+ machine ids, and the values are tuples with the RGBA values.
228
+ default_color:
229
+ The default color to use for job and global nodes. It can be a
230
+ string with a color name or a tuple with the RGBA values.
231
+
232
+ Returns:
233
+ A function that receives a node and returns a tuple with the RGBA
234
+ values of the color to use in the plot.
235
+ """
236
+
237
+ def _get_node_color(node: Node) -> Tuple[float, float, float, float]:
238
+ if node.node_type == NodeType.OPERATION:
239
+ return machine_colors[node.operation.machine_id]
240
+ if node.node_type == NodeType.MACHINE:
241
+ return machine_colors[node.machine_id]
242
+
243
+ return _color_to_rgba(default_color)
244
+
245
+ return _get_node_color
246
+
247
+
248
+ def three_columns_layout(
249
+ job_shop_graph: JobShopGraph,
250
+ *,
251
+ leftmost_position: float = 0.1,
252
+ rightmost_position: float = 0.9,
253
+ topmost_position: float = 1.0,
254
+ bottommost_position: float = 0.0,
255
+ ) -> Dict[Node, Tuple[float, float]]:
256
+ """Generates coordinates for a three-column grid layout.
257
+
258
+ 1. Left column: Machine nodes (M1, M2, etc.)
259
+ 2. Middle column: Operation nodes (O_ij where i=job, j=operation)
260
+ 3. Right column: Job nodes (J1, J2, etc.)
261
+
262
+ The operations are arranged vertically in groups by job, with a global
263
+ node (G) at the bottom.
264
+
265
+ For example, in a 2-machine, 3-job problem:
266
+
267
+ - Machine nodes (M1, M2) appear in the left column where needed
268
+ - Operation nodes (O_11 through O_33) form the central column
269
+ - Job nodes (J1, J2, J3) appear in the right column at the middle of their
270
+ respective operations
271
+ - The global node (G) appears at the bottom of the middle column
272
+
273
+ Args:
274
+ job_shop_graph:
275
+ The job shop graph instance. It should be already initialized with
276
+ the instance with a valid agent-task graph representation.
277
+ leftmost_position:
278
+ The center position of the leftmost column of the layout. It should
279
+ be a float between 0 and 1. The default is 0.1.
280
+ rightmost_position:
281
+ The center position of the rightmost column of the layout. It
282
+ should be a float between 0 and 1. The default is 0.9.
283
+ topmost_position:
284
+ The center position of the topmost node of the layout. It should be
285
+ a float between 0 and 1. The default is 0.9.
286
+ bottommost_position:
287
+ The center position of the bottommost node of the layout. It should
288
+ be a float between 0 and 1. The default is 0.1.
289
+
290
+ Returns:
291
+ A dictionary with the position of each node in the graph. The keys are
292
+ the node ids, and the values are tuples with the x and y coordinates.
293
+ """
294
+
295
+ x_positions = _get_x_positions(leftmost_position, rightmost_position)
296
+
297
+ operation_nodes = [
298
+ node
299
+ for node in job_shop_graph.nodes_by_type[NodeType.OPERATION]
300
+ if not job_shop_graph.is_removed(node)
301
+ ]
302
+ machine_nodes = [
303
+ node
304
+ for node in job_shop_graph.nodes_by_type[NodeType.MACHINE]
305
+ if not job_shop_graph.is_removed(node)
306
+ ]
307
+ job_nodes = [
308
+ node
309
+ for node in job_shop_graph.nodes_by_type[NodeType.JOB]
310
+ if not job_shop_graph.is_removed(node)
311
+ ]
312
+ global_nodes = [
313
+ node
314
+ for node in job_shop_graph.nodes_by_type[NodeType.GLOBAL]
315
+ if not job_shop_graph.is_removed(node)
316
+ ]
317
+
318
+ # job_nodes = job_shop_graph.nodes_by_type[NodeType.JOB]
319
+ # global_nodes = job_shop_graph.nodes_by_type[NodeType.GLOBAL]
320
+
321
+ total_positions = len(operation_nodes) + len(global_nodes) * 2
322
+ y_spacing = (topmost_position - bottommost_position) / total_positions
323
+
324
+ layout: Dict[Node, Tuple[float, float]] = {}
325
+
326
+ machines_spacing_multiplier = len(operation_nodes) // len(machine_nodes)
327
+ layout.update(
328
+ _assign_positions_from_top(
329
+ machine_nodes,
330
+ x_positions["machine"],
331
+ topmost_position,
332
+ y_spacing * machines_spacing_multiplier,
333
+ )
334
+ )
335
+ layout.update(
336
+ (
337
+ _assign_positions_from_top(
338
+ operation_nodes,
339
+ x_positions["operation"],
340
+ topmost_position,
341
+ y_spacing,
342
+ )
343
+ )
344
+ )
345
+
346
+ if global_nodes:
347
+ layout[global_nodes[0]] = (
348
+ x_positions["operation"],
349
+ bottommost_position,
350
+ )
351
+
352
+ if job_nodes:
353
+ job_multiplier = len(operation_nodes) // len(job_nodes)
354
+ layout.update(
355
+ _assign_positions_from_top(
356
+ job_nodes,
357
+ x_positions["job"],
358
+ topmost_position,
359
+ y_spacing * job_multiplier,
360
+ )
361
+ )
362
+ return layout
363
+
364
+
365
+ def _get_x_positions(
366
+ leftmost_position: float, rightmost_position: float
367
+ ) -> Dict[str, float]:
368
+ center_position = (
369
+ leftmost_position + (rightmost_position - leftmost_position) / 2
370
+ )
371
+ return {
372
+ "machine": leftmost_position,
373
+ "operation": center_position,
374
+ "job": rightmost_position,
375
+ }
376
+
377
+
378
+ def _assign_positions_from_top(
379
+ nodes: List[Node],
380
+ x: float,
381
+ top: float,
382
+ y_spacing: float,
383
+ ) -> Dict[Node, Tuple[float, float]]:
384
+ layout: Dict[Node, Tuple[float, float]] = {}
385
+ for i, node in enumerate(nodes):
386
+ y = top - (i + 1) * y_spacing
387
+ layout[node] = (x, y)
388
+
389
+ return layout
@@ -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
@@ -0,0 +1,70 @@
1
+ job_shop_lib/__init__.py,sha256=xXYzjYqn-98QKOMQ4_fOBZYQ6Z6GUydDW5BNXiVSRCE,643
2
+ job_shop_lib/_base_solver.py,sha256=p17XmtufNc9Y481cqZUT45pEkUmmW1HWG53dfhIBJH8,1363
3
+ job_shop_lib/_job_shop_instance.py,sha256=hNQGSJj0rEQpS-YhzwWmM6QzCWp6r--89jkghSgLvUs,18380
4
+ job_shop_lib/_operation.py,sha256=hx2atpP8LPj9fvxpZIfhBFr9Uq6JP-MKAX5JzTvFXso,3847
5
+ job_shop_lib/_schedule.py,sha256=-RdCtTTj-SdNLFucmSVnrCbjZLcBZ4yfhRBdATjAaW8,11292
6
+ job_shop_lib/_scheduled_operation.py,sha256=krjGn47VwsC7bXUTqlUq8Y-DpiSE9q2z8bqwgJVpAZo,2697
7
+ job_shop_lib/benchmarking/__init__.py,sha256=BYCrJUNr_uk2c0xIbDt07OnUMhQx8Dudkukx3TFWxgw,3271
8
+ job_shop_lib/benchmarking/_load_benchmark.py,sha256=Jb6HYGKkub-3uU3l3NreRPE0PU6f0n8G9Mih5vMImOI,2936
9
+ job_shop_lib/benchmarking/benchmark_instances.json,sha256=F9EvyzFwVxiKAN6rQTsrMhsKstmyUmroyWduM7a00KQ,464841
10
+ job_shop_lib/constraint_programming/__init__.py,sha256=kKQRUxxS_nVFUdXGnf4bQOD9mqrXxZZWElS753A4YiA,454
11
+ job_shop_lib/constraint_programming/_ortools_solver.py,sha256=vz_Kg_CmvZ13yGgqi-hZuFkosJR1v449xNaAZV3PhsE,10501
12
+ job_shop_lib/dispatching/__init__.py,sha256=SXVd0Zh6xTp-lNT7c463pii3l168NCZYf-5uOwBI1Fc,1770
13
+ job_shop_lib/dispatching/_dispatcher.py,sha256=HW333xJoupufSUCg-pfaR_4KoQHjQMd4SBXJj-vgE7Y,22349
14
+ job_shop_lib/dispatching/_dispatcher_observer_config.py,sha256=RaUkLxYCHG8Tx2tPgFyOBa8FAcbREZdKuTyLsyaYvhA,2473
15
+ job_shop_lib/dispatching/_factories.py,sha256=1McCm3iJGdGDZerAe6MTWsthpbomuYb3crTKg_5lfyM,4678
16
+ job_shop_lib/dispatching/_history_observer.py,sha256=dixJe83quzGNwG0u0k2uES7GsLw0zWCjX0MOUD4VTRU,634
17
+ job_shop_lib/dispatching/_optimal_operations_observer.py,sha256=HTbmc9EaagM3UA6IIUaj7O0Fq3hCrjONhfonHIZnCE0,4376
18
+ job_shop_lib/dispatching/_ready_operation_filters.py,sha256=H2-CPL0BKDvpLM2zvDGr9eC849qLgbxsVDHgPAHtNgc,5754
19
+ job_shop_lib/dispatching/_unscheduled_operations_observer.py,sha256=3E0ePesDdWdNs6520znnOBW3eiegJj5bZg9Tmb0xoSA,2705
20
+ job_shop_lib/dispatching/feature_observers/__init__.py,sha256=EuJLvSpJpoXUK8A4UuC2k6Mpa293ZR3oCnnvYivIBtU,2240
21
+ job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py,sha256=5njpWV1Biui931HEuik9JGjsawle8fXaU5-mFYsxhRI,7933
22
+ job_shop_lib/dispatching/feature_observers/_duration_observer.py,sha256=fbkUIVScF1iNjdVCYr1ImQm53TfahvVnGXhsRAsgdzY,4129
23
+ job_shop_lib/dispatching/feature_observers/_earliest_start_time_observer.py,sha256=W5Tr81Kme8N-m85jmX7yVc65_xlwNQBvVjnjlL-aq7w,11493
24
+ job_shop_lib/dispatching/feature_observers/_factory.py,sha256=JRQLQ4SEXk7_beyzm0VcGQaL1j8NB1v7ZzsEh7NU3IM,3147
25
+ job_shop_lib/dispatching/feature_observers/_feature_observer.py,sha256=inFim_vKRk9_MTHURkr2ZLP1aJ1AHd3OQXqYg7VvCAo,8722
26
+ job_shop_lib/dispatching/feature_observers/_is_completed_observer.py,sha256=Dm67d90C-a-aZXDtRca1VakuYmLFXIl4vD9upr_1q6Q,4642
27
+ job_shop_lib/dispatching/feature_observers/_is_ready_observer.py,sha256=aP5CpwmCWP4w8J69qAC7QwGRQGMlfNbM31n-BRu92DA,1289
28
+ job_shop_lib/dispatching/feature_observers/_is_scheduled_observer.py,sha256=OcuMUB9_By6ZMtX-1_3z-xaxGbP85a5Zv0ywAv7XxWQ,1491
29
+ job_shop_lib/dispatching/feature_observers/_position_in_job_observer.py,sha256=WRknpQBKXs6h6cXLFJW7ZCvjtU8CPL-iXXNPw3g-mLE,1303
30
+ job_shop_lib/dispatching/feature_observers/_remaining_operations_observer.py,sha256=5V87lCrJUabEe8AkTGXPu5yS8OGxeN8L3-xNyHmdmLs,1441
31
+ job_shop_lib/dispatching/rules/__init__.py,sha256=g3PGvMLMa3WMgNhGSW3S_xkHqoHpW8hr_9JqOfR7Xrk,2140
32
+ job_shop_lib/dispatching/rules/_dispatching_rule_factory.py,sha256=0Hht2i_aGS5hgwPxh_59-ZtNQxgSOwEFclukXH6Ib9U,2942
33
+ job_shop_lib/dispatching/rules/_dispatching_rule_solver.py,sha256=9-UE0HiHCeFXFGqB85cSfduLCEm5k5bJkmIujP-_irg,7321
34
+ job_shop_lib/dispatching/rules/_dispatching_rules_functions.py,sha256=wfBdiKqEQQ8C5Gg_mrWWSuWncPwUkFacjeAQ8D4n9Wc,7648
35
+ job_shop_lib/dispatching/rules/_machine_chooser_factory.py,sha256=QqYXQr7Cp6WsqhLa1oHvR1vnbG8IFmYvxM44dQeLeSE,2362
36
+ job_shop_lib/dispatching/rules/_utils.py,sha256=DFDpRoHb56Rtn01vfN69Bq0X3F8P1EtM6trHx9aXg3U,4643
37
+ job_shop_lib/exceptions.py,sha256=ARzpoZJCvRIvOesCiqqFSRxkv6w9WwEXx0aBP-l2IKA,1597
38
+ job_shop_lib/generation/__init__.py,sha256=tgMVhnh62lkwGKywvingFD9SLhc-vERKiWsS-41qQKA,605
39
+ job_shop_lib/generation/_general_instance_generator.py,sha256=e-NDkH-NoCwa14oADj6n_I7BX5xWWVVzRLvb4rpJ92w,6374
40
+ job_shop_lib/generation/_instance_generator.py,sha256=VV0OKX4JgFq3I1EY6s3LrOdPjM3v4lH6S1hkUebTkFQ,4615
41
+ job_shop_lib/generation/_transformations.py,sha256=X-hTAJVIHZ3bmF1rqS0zCit8r5SGpHpV8Fcl92fejow,5336
42
+ job_shop_lib/generation/_utils.py,sha256=cBhGILE0FE3TqvWoHqpaFEffO8D2fb869pF-BdMlYsg,3617
43
+ job_shop_lib/graphs/__init__.py,sha256=zw4aOE-7QF8Lt8316rCUwOEAGqznjiijumTlGqBmfuw,1840
44
+ job_shop_lib/graphs/_build_disjunctive_graph.py,sha256=UbUYdeQaaeEqLchcKJGHEFGl4wElfGLb1o_R-u8wqnA,5120
45
+ job_shop_lib/graphs/_build_resource_task_graphs.py,sha256=GHUHkUNPxVf1miScgPPMe2YqlXFEMxIy5cDhNw7OZ1E,6954
46
+ job_shop_lib/graphs/_constants.py,sha256=K-GeVvh_DTWpo1KOX1clmxWS_pkUJbq19yOBmrCVIxI,1086
47
+ job_shop_lib/graphs/_job_shop_graph.py,sha256=mgawDY-PlEkGeTxoF0HaZCs0K514YH6zKFB0SssbJak,10761
48
+ job_shop_lib/graphs/_node.py,sha256=9TFH8C1D44W1IvOIG8MucLNQyLzasyBXVkMZTJU4rso,6075
49
+ job_shop_lib/graphs/graph_updaters/__init__.py,sha256=UhnZL55e3cAv7hVetB6bRmIOn8BDhG2bsbrdRoHtxLY,516
50
+ job_shop_lib/graphs/graph_updaters/_graph_updater.py,sha256=j1f7iWsa62GVszK2BPaMxnKBCEGWa9owm8g4VWUje8w,1967
51
+ job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py,sha256=SfgmDyMwfW56OBjJPaU76c42IsX5qx9j-eMtrv0DjKk,6047
52
+ job_shop_lib/graphs/graph_updaters/_utils.py,sha256=X5YfwJA1CCgpm1r9C036Gal2CkDh2SSak7wl7TbdjHw,704
53
+ job_shop_lib/reinforcement_learning/__init__.py,sha256=gOY-C6BMeFr3084MKMMbW0CoK7gMsaOYNsgnYuepswQ,1033
54
+ job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py,sha256=ib1Y6cItVvId4PfesiQ0XKbh9y6h8LVhD0gYDO4wSlk,15732
55
+ job_shop_lib/reinforcement_learning/_reward_observers.py,sha256=iWHccnujeAKyTQn2ilQ4BhcEccoSTyJqQ5yOiP5GG_Y,2984
56
+ job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py,sha256=DZnXeXmzMGKq-vwFhxukmSDN1UyrkUfbnjpjFtC9_Bs,15845
57
+ job_shop_lib/reinforcement_learning/_types_and_constants.py,sha256=xozdM_Wabdbe9e1a769p5980OSNBwQqc9yyaSGW2ODQ,1743
58
+ job_shop_lib/reinforcement_learning/_utils.py,sha256=ksg2ghSncxd0K3AR5hGS5PQejjd-Hgx6LGtXX_oatOc,2523
59
+ job_shop_lib/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
+ job_shop_lib/visualization/gantt/__init__.py,sha256=HGXwRgDuMAldqU0JBdiZCd5e79XBz1r96qHeDVlzE54,1145
61
+ job_shop_lib/visualization/gantt/_gantt_chart_creator.py,sha256=LTsVhpB1Fb_2o08HRZPPXSekwzR7fyTSC6h549XMqhU,8638
62
+ job_shop_lib/visualization/gantt/_gantt_chart_video_and_gif_creation.py,sha256=CcHcvafYrTy7UaScM_wp9QlLOgKiTIKV7tFkttMgoLU,14474
63
+ job_shop_lib/visualization/gantt/_plot_gantt_chart.py,sha256=9-NSSNsVcW8gYLZtAuFeYURqi8cHNkVYufosKtbKFOI,6881
64
+ job_shop_lib/visualization/graphs/__init__.py,sha256=282hZFg07EyQu4HVt4GzFfYnY6ZF376IMjnWZ5eg0ZQ,611
65
+ job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py,sha256=4VBMYiFXXkCGSnGYN9iqNtWrbLJQxAMHojPHhAbdA0s,14387
66
+ job_shop_lib/visualization/graphs/_plot_resource_task_graph.py,sha256=RgJqHS5hJh3KkyaLbtpG_bER981BFRwGpflz7I7gS64,13271
67
+ job_shop_lib-1.0.0b2.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
68
+ job_shop_lib-1.0.0b2.dist-info/METADATA,sha256=_6waZfwgtsPOyvZ5rw18aulHrqEOJji5jcde9NrQBpg,16978
69
+ job_shop_lib-1.0.0b2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
70
+ job_shop_lib-1.0.0b2.dist-info/RECORD,,