job-shop-lib 1.0.0a3__py3-none-any.whl → 1.0.0a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. job_shop_lib/_job_shop_instance.py +104 -38
  2. job_shop_lib/_operation.py +12 -3
  3. job_shop_lib/_schedule.py +10 -12
  4. job_shop_lib/_scheduled_operation.py +15 -16
  5. job_shop_lib/dispatching/_dispatcher.py +12 -15
  6. job_shop_lib/dispatching/_dispatcher_observer_config.py +15 -2
  7. job_shop_lib/dispatching/_factories.py +2 -2
  8. job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +0 -1
  9. job_shop_lib/dispatching/feature_observers/_factory.py +21 -18
  10. job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +1 -0
  11. job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +1 -1
  12. job_shop_lib/generation/_general_instance_generator.py +33 -34
  13. job_shop_lib/generation/_instance_generator.py +14 -17
  14. job_shop_lib/generation/_transformations.py +11 -8
  15. job_shop_lib/graphs/__init__.py +3 -0
  16. job_shop_lib/graphs/_build_disjunctive_graph.py +41 -3
  17. job_shop_lib/graphs/graph_updaters/_graph_updater.py +11 -13
  18. job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +17 -20
  19. job_shop_lib/reinforcement_learning/__init__.py +16 -7
  20. job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +69 -57
  21. job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +42 -31
  22. job_shop_lib/reinforcement_learning/_types_and_constants.py +2 -2
  23. job_shop_lib/visualization/__init__.py +24 -5
  24. job_shop_lib/visualization/_gantt_chart_creator.py +118 -80
  25. job_shop_lib/visualization/_gantt_chart_video_and_gif_creation.py +15 -11
  26. job_shop_lib/visualization/_plot_disjunctive_graph.py +382 -0
  27. {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/METADATA +5 -5
  28. {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/RECORD +31 -31
  29. job_shop_lib/visualization/_disjunctive_graph.py +0 -210
  30. /job_shop_lib/visualization/{_agent_task_graph.py → _plot_agent_task_graph.py} +0 -0
  31. {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/LICENSE +0 -0
  32. {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/WHEEL +0 -0
@@ -1,210 +0,0 @@
1
- """Module for visualizing the disjunctive graph of a job shop instance."""
2
-
3
- import functools
4
- from typing import Optional, Callable
5
- import warnings
6
- import copy
7
-
8
- import matplotlib
9
- import matplotlib.pyplot as plt
10
- import networkx as nx
11
- from networkx.drawing.nx_agraph import graphviz_layout
12
-
13
- from job_shop_lib import JobShopInstance
14
- from job_shop_lib.graphs import (
15
- JobShopGraph,
16
- EdgeType,
17
- NodeType,
18
- Node,
19
- build_disjunctive_graph,
20
- )
21
-
22
-
23
- Layout = Callable[[nx.Graph], dict[str, tuple[float, float]]]
24
-
25
-
26
- # This function could be improved by a function extraction refactoring
27
- # (see `plot_gantt_chart`
28
- # function as a reference in how to do it). That would solve the
29
- # "too many locals" warning. However, this refactoring is not a priority at
30
- # the moment. To compensate, sections are separated by comments.
31
- # For the "too many arguments" warning no satisfactory solution was
32
- # found. I believe is still better than using `**kwargs` and losing the
33
- # function signature or adding a dataclass for configuration (it would add
34
- # unnecessary complexity).
35
- # pylint: disable=too-many-arguments, too-many-locals
36
- def plot_disjunctive_graph(
37
- job_shop: JobShopGraph | JobShopInstance,
38
- figsize: tuple[float, float] = (6, 4),
39
- node_size: int = 1600,
40
- title: Optional[str] = None,
41
- layout: Optional[Layout] = None,
42
- edge_width: int = 2,
43
- font_size: int = 10,
44
- arrow_size: int = 35,
45
- alpha=0.95,
46
- node_font_color: str = "white",
47
- color_map: str = "Dark2_r",
48
- draw_disjunctive_edges: bool = True,
49
- ) -> plt.Figure:
50
- """Returns a plot of the disjunctive graph of the instance."""
51
-
52
- if isinstance(job_shop, JobShopInstance):
53
- job_shop_graph = build_disjunctive_graph(job_shop)
54
- else:
55
- job_shop_graph = job_shop
56
-
57
- # Set up the plot
58
- # ----------------
59
- plt.figure(figsize=figsize)
60
- if title is None:
61
- title = (
62
- f"Disjunctive Graph Visualization: {job_shop_graph.instance.name}"
63
- )
64
- plt.title(title)
65
-
66
- # Set up the layout
67
- # -----------------
68
- if layout is None:
69
- layout = functools.partial(
70
- graphviz_layout, prog="dot", args="-Grankdir=LR"
71
- )
72
-
73
- temp_graph = copy.deepcopy(job_shop_graph.graph)
74
- # Remove disjunctive edges to get a better layout
75
- temp_graph.remove_edges_from(
76
- [
77
- (u, v)
78
- for u, v, d in job_shop_graph.graph.edges(data=True)
79
- if d["type"] == EdgeType.DISJUNCTIVE
80
- ]
81
- )
82
-
83
- try:
84
- pos = layout(temp_graph)
85
- except ImportError:
86
- warnings.warn(
87
- "Default layout requires pygraphviz http://pygraphviz.github.io/. "
88
- "Using spring layout instead.",
89
- )
90
- pos = nx.spring_layout(temp_graph)
91
-
92
- # Draw nodes
93
- # ----------
94
- node_colors = [
95
- _get_node_color(node)
96
- for node in job_shop_graph.nodes
97
- if not job_shop_graph.is_removed(node.node_id)
98
- ]
99
-
100
- nx.draw_networkx_nodes(
101
- job_shop_graph.graph,
102
- pos,
103
- node_size=node_size,
104
- node_color=node_colors,
105
- alpha=alpha,
106
- cmap=matplotlib.colormaps.get_cmap(color_map),
107
- )
108
-
109
- # Draw edges
110
- # ----------
111
- conjunctive_edges = [
112
- (u, v)
113
- for u, v, d in job_shop_graph.graph.edges(data=True)
114
- if d["type"] == EdgeType.CONJUNCTIVE
115
- ]
116
- disjunctive_edges = [
117
- (u, v)
118
- for u, v, d in job_shop_graph.graph.edges(data=True)
119
- if d["type"] == EdgeType.DISJUNCTIVE
120
- ]
121
-
122
- nx.draw_networkx_edges(
123
- job_shop_graph.graph,
124
- pos,
125
- edgelist=conjunctive_edges,
126
- width=edge_width,
127
- edge_color="black",
128
- arrowsize=arrow_size,
129
- )
130
-
131
- if draw_disjunctive_edges:
132
- nx.draw_networkx_edges(
133
- job_shop_graph.graph,
134
- pos,
135
- edgelist=disjunctive_edges,
136
- width=edge_width,
137
- edge_color="red",
138
- arrowsize=arrow_size,
139
- )
140
-
141
- # Draw node labels
142
- # ----------------
143
- operation_nodes = job_shop_graph.nodes_by_type[NodeType.OPERATION]
144
-
145
- labels = {}
146
- source_node = job_shop_graph.nodes_by_type[NodeType.SOURCE][0]
147
- labels[source_node] = "S"
148
-
149
- sink_node = job_shop_graph.nodes_by_type[NodeType.SINK][0]
150
- labels[sink_node] = "T"
151
- for operation_node in operation_nodes:
152
- if job_shop_graph.is_removed(operation_node.node_id):
153
- continue
154
- labels[operation_node] = (
155
- f"m={operation_node.operation.machine_id}\n"
156
- f"d={operation_node.operation.duration}"
157
- )
158
-
159
- nx.draw_networkx_labels(
160
- job_shop_graph.graph,
161
- pos,
162
- labels=labels,
163
- font_color=node_font_color,
164
- font_size=font_size,
165
- font_family="sans-serif",
166
- )
167
-
168
- # Final touches
169
- # -------------
170
- plt.axis("off")
171
- plt.tight_layout()
172
- # Create a legend to indicate the meaning of the edge colors
173
- conjunctive_patch = matplotlib.patches.Patch(
174
- color="black", label="conjunctive edges"
175
- )
176
- disjunctive_patch = matplotlib.patches.Patch(
177
- color="red", label="disjunctive edges"
178
- )
179
-
180
- # Add to the legend the meaning of m and d
181
- text = "m = machine_id\nd = duration"
182
- extra = matplotlib.patches.Rectangle(
183
- (0, 0),
184
- 1,
185
- 1,
186
- fc="w",
187
- fill=False,
188
- edgecolor="none",
189
- linewidth=0,
190
- label=text,
191
- )
192
- plt.legend(
193
- handles=[conjunctive_patch, disjunctive_patch, extra],
194
- loc="upper left",
195
- bbox_to_anchor=(1.05, 1),
196
- borderaxespad=0.0,
197
- )
198
- return plt.gcf()
199
-
200
-
201
- def _get_node_color(node: Node) -> int:
202
- """Returns the color of the node."""
203
- if node.node_type == NodeType.SOURCE:
204
- return -1
205
- if node.node_type == NodeType.SINK:
206
- return -1
207
- if node.node_type == NodeType.OPERATION:
208
- return node.operation.machine_id
209
-
210
- raise ValueError("Invalid node type.")