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.
- job_shop_lib/_job_shop_instance.py +104 -38
- job_shop_lib/_operation.py +12 -3
- job_shop_lib/_schedule.py +10 -12
- job_shop_lib/_scheduled_operation.py +15 -16
- job_shop_lib/dispatching/_dispatcher.py +12 -15
- job_shop_lib/dispatching/_dispatcher_observer_config.py +15 -2
- job_shop_lib/dispatching/_factories.py +2 -2
- job_shop_lib/dispatching/feature_observers/_composite_feature_observer.py +0 -1
- job_shop_lib/dispatching/feature_observers/_factory.py +21 -18
- job_shop_lib/dispatching/feature_observers/_is_completed_observer.py +1 -0
- job_shop_lib/dispatching/rules/_dispatching_rule_solver.py +1 -1
- job_shop_lib/generation/_general_instance_generator.py +33 -34
- job_shop_lib/generation/_instance_generator.py +14 -17
- job_shop_lib/generation/_transformations.py +11 -8
- job_shop_lib/graphs/__init__.py +3 -0
- job_shop_lib/graphs/_build_disjunctive_graph.py +41 -3
- job_shop_lib/graphs/graph_updaters/_graph_updater.py +11 -13
- job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py +17 -20
- job_shop_lib/reinforcement_learning/__init__.py +16 -7
- job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py +69 -57
- job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py +42 -31
- job_shop_lib/reinforcement_learning/_types_and_constants.py +2 -2
- job_shop_lib/visualization/__init__.py +24 -5
- job_shop_lib/visualization/_gantt_chart_creator.py +118 -80
- job_shop_lib/visualization/_gantt_chart_video_and_gif_creation.py +15 -11
- job_shop_lib/visualization/_plot_disjunctive_graph.py +382 -0
- {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/METADATA +5 -5
- {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/RECORD +31 -31
- job_shop_lib/visualization/_disjunctive_graph.py +0 -210
- /job_shop_lib/visualization/{_agent_task_graph.py → _plot_agent_task_graph.py} +0 -0
- {job_shop_lib-1.0.0a3.dist-info → job_shop_lib-1.0.0a4.dist-info}/LICENSE +0 -0
- {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.")
|
File without changes
|
File without changes
|
File without changes
|