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

Sign up to get free protection for your applications and to get access to all the features.
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.")