Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
visualization.py
Go to the documentation of this file.
1# Copyright 2010-2024 Google LLC
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13
14"""Collection of helpers to visualize cp_model solutions in colab."""
15
16# pylint: disable=g-import-not-at-top
17import random
18
19try:
20 from IPython.display import display
21 from IPython.display import SVG
22 import plotly.figure_factory as ff
23 import svgwrite
24
25 correct_imports = True
26except ImportError:
27 correct_imports = False
28
29
31 if not correct_imports:
32 return False
33 try:
34 return __IPYTHON__ is not None
35 except NameError:
36 return False
37
38
39def ToDate(v):
40 return "2016-01-01 6:%02i:%02i" % (v / 60, v % 60)
41
42
44 """Utility to create colors to use in visualization."""
45
46 def ScaledColor(self, sr, sg, sb, er, eg, eb, num_steps, step):
47 """Creates an interpolated rgb color between two rgb colors."""
48 num_intervals = num_steps - 1
49 dr = (er - sr) / num_intervals
50 dg = (eg - sg) / num_intervals
51 db = (eb - sb) / num_intervals
52 r = sr + dr * step
53 g = sg + dg * step
54 b = sb + db * step
55 return "rgb(%i, %i, %i)" % (r, g, b)
56
57 def SeedRandomColor(self, seed=0):
58 random.seed(seed)
59
60 def RandomColor(self):
61 return "rgb(%i,%i,%i)" % (
62 random.randint(0, 255),
63 random.randint(0, 255),
64 random.randint(0, 255),
65 )
66
67
68def DisplayJobshop(starts, durations, machines, name):
69 """Simple function to display a jobshop solution using plotly."""
70
71 jobs_count = len(starts)
72 machines_count = len(starts[0])
73 all_machines = range(0, machines_count)
74 all_jobs = range(0, jobs_count)
75 df = []
76 for i in all_jobs:
77 for j in all_machines:
78 df.append(
79 dict(
80 Task="Resource%i" % machines[i][j],
81 Start=ToDate(starts[i][j]),
82 Finish=ToDate(starts[i][j] + durations[i][j]),
83 Resource="Job%i" % i,
84 )
85 )
86
87 sorted_df = sorted(df, key=lambda k: k["Task"])
88
89 colors = {}
90 cm = ColorManager()
91 cm.SeedRandomColor(0)
92 for i in all_jobs:
93 colors["Job%i" % i] = cm.RandomColor()
94
95 fig = ff.create_gantt(
96 sorted_df,
97 colors=colors,
98 index_col="Resource",
99 title=name,
100 show_colorbar=False,
101 showgrid_x=True,
102 showgrid_y=True,
103 group_tasks=True,
104 )
105 fig.show()
106
107
109 """Simple SVG wrapper to use in colab."""
110
111 def __init__(self, sizex, sizey, scaling=20.0):
112 self.__sizex = sizex
113 self.__sizey = sizey
114 self.__scaling = scaling
115 self.__offset = scaling
116 self.__dwg = svgwrite.Drawing(
117 size=(
118 self.__sizex * self.__scaling + self.__offset,
119 self.__sizey * self.__scaling + self.__offset * 2,
120 )
121 )
122
123 def Display(self):
124 display(SVG(self.__dwg.tostring()))
125
126 def AddRectangle(self, x, y, dx, dy, fill, stroke="black", label=None):
127 """Draw a rectangle, dx and dy must be >= 0."""
128 s = self.__scaling
129 o = self.__offset
130 corner = (x * s + o, (self.__sizey - y - dy) * s + o)
131 size = (dx * s - 1, dy * s - 1)
132 self.__dwg.add(
133 self.__dwg.rect(insert=corner, size=size, fill=fill, stroke=stroke)
134 )
135 self.AddText(x + 0.5 * dx, y + 0.5 * dy, label)
136
137 def AddText(self, x, y, label):
138 text = self.__dwg.text(
139 label,
140 insert=(
141 x * self.__scaling + self.__offset,
142 (self.__sizey - y) * self.__scaling + self.__offset,
143 ),
144 text_anchor="middle",
145 font_family="sans-serif",
146 font_size="%dpx" % (self.__scaling / 2),
147 )
148 self.__dwg.add(text)
149
150 def AddXScale(self, step=1):
151 """Add an scale on the x axis."""
152 o = self.__offset
153 s = self.__scaling
154 y = self.__sizey * s + o / 2.0 + o
155 dy = self.__offset / 4.0
156 self.__dwg.add(
157 self.__dwg.line((o, y), (self.__sizex * s + o, y), stroke="black")
158 )
159 for i in range(0, int(self.__sizex) + 1, step):
160 self.__dwg.add(
161 self.__dwg.line(
162 (o + i * s, y - dy), (o + i * s, y + dy), stroke="black"
163 )
164 )
165
166 def AddYScale(self, step=1):
167 """Add an scale on the y axis."""
168 o = self.__offset
169 s = self.__scaling
170 x = o / 2.0
171 dx = self.__offset / 4.0
172 self.__dwg.add(
173 self.__dwg.line((x, o), (x, self.__sizey * s + o), stroke="black")
174 )
175 for i in range(0, int(self.__sizey) + 1, step):
176 self.__dwg.add(
177 self.__dwg.line(
178 (x - dx, i * s + o), (x + dx, i * s + o), stroke="black"
179 )
180 )
181
182 def AddTitle(self, title):
183 """Add a title to the drawing."""
184 text = self.__dwg.text(
185 title,
186 insert=(
187 self.__offset + self.__sizex * self.__scaling / 2.0,
188 self.__offset / 2,
189 ),
190 text_anchor="middle",
191 font_family="sans-serif",
192 font_size="%dpx" % (self.__scaling / 2),
193 )
194 self.__dwg.add(text)
ScaledColor(self, sr, sg, sb, er, eg, eb, num_steps, step)
AddRectangle(self, x, y, dx, dy, fill, stroke="black", label=None)
__init__(self, sizex, sizey, scaling=20.0)
DisplayJobshop(starts, durations, machines, name)