src: clean white-spaces and license in some files
[nit.git] / src / coloring.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 module coloring
16
17 import poset
18
19 # Build a conflict graph from a POSet
20 class POSetConflictGraph[E: Object]
21
22 # Core is composed by:
23 # * elements that have mutiple direct parents
24 # * parents of elements that have multiple direct parents
25 # REQUIRE: is_colored
26 var core = new HashSet[E]
27
28 # Border is composed by minimal elements of the core:
29 # * that have multiple direct parents
30 # * but whose subelements are all in single inheritance
31 # REQUIRE: is_colored
32 var border = new HashSet[E]
33
34 # The crown is composed by the elements that are:
35 # * not part of the core nor the border
36 # * are in single inheritance
37 # REQUIRE: is_colored
38 var crown = new HashSet[E]
39
40 # Conflict graph of the POSet
41 # Elements X and Y are in conflict if either:
42 # * X and Y are the same element
43 # * Y is a subelement of X
44 # * X and Y have common sub elements
45 # REQUIRE: is_colored
46 var conflicts = new HashMap[E, Set[E]]
47
48 var poset: POSet[E]
49
50 init(poset: POSet[E]) do
51 self.poset = poset
52 extract_core
53 extract_border
54 extract_crown
55 compute_conflicts
56 end
57
58 # Compute the set of elements forming the core of the poset hierarchy.
59 private fun extract_core do
60 core.clear
61 for e in poset do
62 if poset[e].direct_greaters.length > 1 then
63 core.add_all(poset[e].greaters)
64 end
65 end
66 end
67
68 # Compute the set of elements composing the border of the core
69 # Elements belonging to the `border` are removed from the `core`
70 private fun extract_border do
71 border.clear
72 for e in core do
73 if not is_border(e) then continue
74 border.add(e)
75 end
76 for e in border do core.remove(e)
77 end
78
79 private fun is_border(e: E): Bool do
80 for child in poset[e].direct_smallers do
81 if core.has(child) then return false
82 end
83 return true
84 end
85
86 # Compute the set of elements belonging to the crown of the inheritance hierarchy.
87 private fun extract_crown do
88 crown.clear
89 for e in poset do
90 if not core.has(e) and not border.has(e) then crown.add(e)
91 end
92 end
93
94 # Check for conflict in the core.
95 # Start from border and tag every ancestors
96 private fun compute_conflicts do
97 conflicts.clear
98 for e in border do add_conflicts(poset[e].greaters)
99 end
100
101 private fun add_conflict(e, o: E) do
102 if not conflicts.has_key(e) then conflicts[e] = new HashSet[E]
103 if not conflicts.has_key(o) then conflicts[o] = new HashSet[E]
104 conflicts[e].add(o)
105 conflicts[o].add(e)
106 end
107
108 private fun add_conflicts(es: Collection[E]) do
109 for e1 in es do
110 for e2 in es do add_conflict(e1, e2)
111 end
112 end
113
114 # Used for debugging only
115 fun pretty_print do
116 #print "core: {core.join(" ")} ({core.length})"
117 #print "border: {border.join(" ")} ({border.length})"
118 #print "crown: {crown.join(" ")} ({crown.length})"
119 print "conflicts:"
120 for e, c in conflicts do print " {e}: {c.join(" ")}"
121 end
122 end
123
124 # Colorize elements from a POSet
125 # Two elements from a POSet cannot have the same color if they share common subelements
126 #
127 # Example:
128 # A
129 # / | \
130 # / | \
131 # B C D
132 # | /| |
133 # | / | |
134 # |/ | |
135 # E F G
136 # |
137 # H
138 # Conflicts:
139 # A: {B, C, D, E, F, G, H}
140 # B: {A, C, E, H}
141 # C: {A, E, H, F}
142 # D: {A, G}
143 # E: {A, B, C, H}
144 # F: {A, C}
145 # G: {A, D}
146 # H: {A, B, C, E}
147 # Possible colors:
148 # A:0, B:1, C: 2, D: 1, E: 3, F:3, G:2, H:4
149 #
150 # see:
151 # Ducournau, R. (2011).
152 # Coloring, a versatile technique for implementing object-oriented languages.
153 # Software: Practice and Experience, 41(6), 627–659.
154 class POSetColorer[E: Object]
155
156 # Is the poset already colored?
157 var is_colored = false
158
159 # Resulting ids
160 # REQUIRE: is_colored
161 fun ids: Map[E, Int] do
162 assert is_colored
163 return ids_cache
164 end
165 private var ids_cache = new HashMap[E, Int]
166
167 # Resulting colors
168 # REQUIRE: is_colored
169 fun colors: Map[E, Int] do
170 assert is_colored
171 return colors_cache
172 end
173 private var colors_cache = new HashMap[E, Int]
174
175 # REQUIRE: is_colored
176 fun poset: POSet[E] do
177 assert is_colored
178 return poset_cache
179 end
180 private var poset_cache: POSet[E] is noinit
181
182 # REQUIRE: is_colored
183 fun conflicts: Map[E, Set[E]] do
184 assert is_colored
185 return conflicts_cache
186 end
187 private var conflicts_cache: Map[E, Set[E]] is noinit
188
189 private var graph: POSetConflictGraph[E] is noinit
190
191 # Start coloring on given POSet
192 fun colorize(poset: POSet[E]) do
193 poset_cache = poset
194 graph = new POSetConflictGraph[E](poset)
195 allocate_ids
196 compute_colors
197 conflicts_cache = graph.conflicts
198 is_colored = true
199 end
200
201 private fun allocate_ids do
202 ids_cache.clear
203 var elements = new HashSet[E].from(poset_cache.to_a)
204 for e in poset_cache.linearize(elements) do
205 ids_cache[e] = ids_cache.length
206 end
207 end
208
209 # Colorize core, border and crown in that order
210 private fun compute_colors do
211 colors_cache.clear
212 colorize_core
213 colorize_set(graph.border)
214 colorize_set(graph.crown)
215 end
216
217 # Core elements cannot have the same color than:
218 # * one of their parents
219 # * one of their conflicting elements
220 private fun colorize_core do
221 for e in poset_cache.linearize(graph.core) do
222 var color = min_color(e)
223 var conflicts = graph.conflicts[e]
224 while not is_color_free(color, conflicts) do
225 color += 1
226 end
227 colors_cache[e] = color
228 end
229 end
230
231 # Other elements inherit color fron their direct parents
232 private fun colorize_set(set: Set[E]) do
233 for e in poset_cache.linearize(set) do colors_cache[e] = min_color(e)
234 end
235
236 # Get the next minimal color from direct parents
237 private fun min_color(e: E): Int do
238 var max_color = -1
239 for p in poset_cache[e].direct_greaters do
240 if not colors_cache.has_key(p) then continue
241 var color = colors_cache[p]
242 if color > max_color then max_color = color
243 end
244 return max_color + 1
245 end
246
247 private fun is_color_free(color: Int, set: Collection[E]): Bool do
248 for e in set do
249 if colors_cache.has_key(e) and colors_cache[e] == color then return false
250 end
251 return true
252 end
253
254 # Used for debugging only
255 fun pretty_print do
256 print "ids:"
257 for e, id in ids do print " {e}: {id}"
258 print "colors:"
259 for e, c in colors do print " {e}: {c}"
260 end
261 end
262
263 # Colorize a collection of buckets
264 # Two elements cannot have the same color if they both appear in the same bucket
265 # No coloring order is garantied
266 #
267 # Example:
268 # buckets[A] = {x1, x2}
269 # buckets[B] = {x1, x3, x4}
270 # buckets[C] = {x2, x3}
271 # Conflicts:
272 # x1: {x2, x3, x4}
273 # x2: {x1, x3}
274 # x3: {x1, x2, x4}
275 # x4: {x1, x3}
276 # Possible colors:
277 # x1: 0, x2: 1, x3: 2, x4: 1
278 class BucketsColorer[H: Object, E: Object]
279 private var colors = new HashMap[E, Int]
280 private var conflicts = new HashMap[E, Set[E]]
281
282 init do end
283
284 # Start bucket coloring
285 fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
286 compute_conflicts(buckets)
287 var min_color = 0
288 for holder, hbuckets in buckets do
289 for bucket in hbuckets do
290 if colors.has_key(bucket) then continue
291 var color = min_color
292 while not is_color_free(bucket, color) do
293 color += 1
294 end
295 colors[bucket] = color
296 color = min_color
297 end
298 end
299 return colors
300 end
301
302 private fun is_color_free(bucket: E, color: Int): Bool do
303 if conflicts.has_key(bucket) then
304 for other in conflicts[bucket] do
305 if colors.has_key(other) and colors[other] == color then return false
306 end
307 end
308 return true
309 end
310
311 private fun compute_conflicts(buckets: Map[H, Set[E]]) do
312 conflicts.clear
313 for holder, hbuckets in buckets do
314 for bucket in hbuckets do
315 if not conflicts.has_key(bucket) then conflicts[bucket] = new HashSet[E]
316 for obucket in hbuckets do
317 if obucket == bucket then continue
318 if not conflicts.has_key(obucket) then conflicts[obucket] = new HashSet[E]
319 conflicts[bucket].add(obucket)
320 conflicts[obucket].add(bucket)
321 end
322 end
323 end
324 end
325 end
326
327 # Colorize a collection of buckets using a poset and a conflict graph
328 # Two elements cannot have the same color if they both appear in the same bucket
329 # The use of a POSet hierarchy optimize the coloring
330 # Buckets elements are colored using linearization order starting
331 class POSetBucketsColorer[H: Object, E: Object]
332 private var colors = new HashMap[E, Int]
333 private var poset: POSet[H]
334 private var conflicts: Map[H, Set[H]]
335
336 init(poset: POSet[H], conflicts: Map[H, Set[H]]) do
337 self.poset = poset
338 self.conflicts = conflicts
339 end
340
341 # Colorize buckets using the POSet and conflict graph
342 fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
343 colors.clear
344 for h in poset.linearize(buckets.keys) do
345 var color = min_color(poset[h].direct_greaters, buckets)
346 for bucket in buckets[h] do
347 if colors.has_key(bucket) then continue
348 while not is_color_free(color, h, buckets) do color += 1
349 colors[bucket] = color
350 color += 1
351 end
352 end
353 return colors
354 end
355
356 # Get the next available color considering used colors by other buckets
357 private fun min_color(others: Collection[H], buckets: Map[H, Set[E]]): Int do
358 var min = -1
359 for holder in others do
360 var color = max_color(holder, buckets)
361 if color > min then min = color
362 end
363 return min + 1
364 end
365
366 # Return the max color used by a class
367 private fun max_color(holder: H, buckets: Map[H, Set[E]]): Int do
368 var max = -1
369 for bucket in buckets[holder] do
370 if not colors.has_key(bucket) then continue
371 var color = colors[bucket]
372 if color > max then max = color
373 end
374 return max
375 end
376
377 # Check if the color is free for this holder
378 private fun is_color_free(color: Int, holder: H, buckets: Map[H, Set[E]]): Bool do
379 if not conflicts.has_key(holder) then return true
380 for conflict in conflicts[holder] do
381 for bucket in buckets[conflict] do
382 if not colors.has_key(bucket) then continue
383 if colors[bucket] == color then return false
384 end
385 end
386 return true
387 end
388 end