Coverage for moptipyapps / binpacking2d / bks.py: 48%
359 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
1"""In this file, we provide a set of best-known solutions for the 2D-BPP."""
3from dataclasses import dataclass
4from importlib import resources # nosem
5from math import inf, isfinite
6from re import Pattern
7from re import compile as p_compile
8from statistics import mean
9from typing import Any, Callable, Final, Iterable, Mapping, cast
11from pycommons.ds.immutable_map import immutable_mapping
12from pycommons.io.path import UTF8
13from pycommons.math.int_math import try_int, try_int_mul
14from pycommons.strings.chars import WHITESPACE
15from pycommons.strings.string_tools import replace_regex
16from pycommons.types import check_int_range
18from moptipyapps.binpacking2d.instance import Instance
19from moptipyapps.binpacking2d.packing_result import (
20 BIN_COUNT_NAME,
21 PackingResult,
22)
25def __load_references() -> Mapping[str, str]:
26 """
27 Load the references from the BibTeX resource.
29 This is not a fully-fledged BibTeX parser. It is a very simple and crude
30 method to extract bibliography keys and data.
32 :return: the immutable reference dictionary, with keys sorted
33 alphabetically
34 """
35 current_lines: list[str] = []
36 key: str | None = None
37 mode: int = 0
38 found: Final[dict[str, str]] = {}
39 single_ws: Final[Pattern] = p_compile(f"[{WHITESPACE}]")
40 multi_ws: Final[Pattern] = p_compile(f"[{WHITESPACE}][{WHITESPACE}]+")
41 with resources.files(__package__).joinpath(
42 "bks.bib").open("r", encoding=UTF8) as stream:
43 for rline in stream:
44 line: str = str.rstrip(rline)
45 if str.__len__(line) <= 0:
46 continue
47 if mode == 0:
48 line = replace_regex(single_ws, "", line)
49 if str.startswith(line, "@"):
50 mode = 1
51 start_idx: int = line.index("{")
52 end_idx: int = line.index(",", start_idx + 1)
53 key = str.strip(line[start_idx + 1:end_idx])
54 if str.__len__(key) <= 0:
55 raise ValueError(f"Invalid key {key!r} in {line!r}.")
56 current_lines.append(line)
57 if key in found:
58 raise ValueError(f"Duplicate key {key!r}.")
59 elif mode == 1:
60 if str.strip(line) in {"}", "},"}:
61 mode = 0
62 current_lines.append("}")
63 found[key] = str.strip("\n".join(current_lines))
64 current_lines.clear()
65 else:
66 current_lines.append(replace_regex(multi_ws, " ", line))
67 if dict.__len__(found) <= 0:
68 raise ValueError("Found no references!")
69 return immutable_mapping({k: found[k] for k in sorted(found.keys())})
72#: the references to literature
73_REFERENCES: Final[Mapping[str, str]] = __load_references()
75#: a constant for no reference
76NO_REF: Final[str] = "{{NO_REF}}"
79@dataclass(frozen=True, init=False, order=True, eq=True)
80class Element:
81 """The algorithm or instance group specification."""
83 #: the name or name prefix
84 name: str
85 #: the name suffix, if any, and the empty string otherwise
86 name_suffix: str
87 #: the reference to the related work
88 reference: str
90 def __init__(self, name: str,
91 name_suffix: str, reference: str = NO_REF) -> None:
92 """
93 Initialize this element.
95 :param name: the name or name prefix
96 :param name_suffix: the name suffix, or an empty string
97 :param reference: the reference
98 """
99 name = str.strip(name)
100 if str.__len__(name) <= 0:
101 raise ValueError(f"Invalid name {name!r}.")
102 object.__setattr__(self, "name", name)
103 object.__setattr__(self, "name_suffix", str.strip(name_suffix))
104 reference = str.strip(reference)
105 for ref in reference.split(","):
106 if ref == NO_REF:
107 continue
108 if ref not in _REFERENCES:
109 raise ValueError(f"Invalid reference {ref!r}.")
110 object.__setattr__(self, "reference", reference)
112 def get_bibtex(self) -> Iterable[str]:
113 """
114 Get the BibTeX for this element.
116 :return: the BibTeX string
117 """
118 return (_REFERENCES[r] for r in self.reference.split(","))
121#: the BRKGA for 2D bin packing with rotation
122BRKGA_BPP_2R: Final[Element] = Element(
123 "BRKGA", "BRKGABPPRTR", "GR2013ABRKGAF2A3BPP")
124#: the BRKGA for 2D bin packing without rotation
125BRKGA_BPP_ANB: Final[Element] = Element(
126 "BRKGA", "BRKGABPPRANB", "GR2013ABRKGAF2A3BPP")
127#: the grasp/vnd for 2D bin packing without rotation
128GRASP_VND: Final[Element] = Element(
129 "GRASPVNDGRASP", "GRASPVNDVND", "PAVOT2010AHGVAFTATDBP")
130#: the price-and-cut algorithm for 2D bin packing with rotation
131PAC: Final[Element] = Element("PAC", "", "CGRS2020PACATSMTOOSFT2BPP")
132#: the HHANO-R algorithm for 2D bin packing with rotation
133HHANO_R: Final[Element] = Element("HHANO", "HHANOR", "BDC2015RHHAFTOONO2BPP")
134#: the HHANO-SR algorithm for 2D bin packing with rotation
135HHANO_SR: Final[Element] = Element("HHANO", "HHANOSR", "BDC2015RHHAFTOONO2BPP")
136#: the MXGA algorithm for 2D bin packing with rotation
137MXGA: Final[Element] = Element("MXGA", "", "L2008AGAFTDBPP")
138#: the LGFi algorithm
139EALGFI: Final[Element] = Element(
140 "EALGFIEA", "EALGFILGFI", "BS2013ST2BPPBMOAHEA")
142#: the small A instances
143A_SMALL: Final[Element] = Element("a", "small", "MAVdC2010AFMFTTDGCSP")
144#: the median sized A instances
145A_MED: Final[Element] = Element("a", "med", "MAVdC2010AFMFTTDGCSP")
146#: the large A instances
147A_LARGE: Final[Element] = Element("a", "large", "MAVdC2010AFMFTTDGCSP")
148#: the asqas
149ASQAS: Final[Element] = Element("asqas", "", "vdBBMSB2016ASIASSTFI")
150#: the first part of the Beng instances
151BENG_1_8: Final[Element] = Element("beng", "1-8", "B1982PRPAHA")
152#: the second part of the Beng instances
153BENG_9_10: Final[Element] = Element("beng", "9-10", "B1982PRPAHA")
154#: the class 1-20 benchmarks
155CLASS_1_20: Final[Element] = Element(
156 "class 1", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
157#: the class 1-40 benchmarks
158CLASS_1_40: Final[Element] = Element(
159 "class 1", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
160#: the class 1-60 benchmarks
161CLASS_1_60: Final[Element] = Element(
162 "class 1", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
163#: the class 1-80 benchmarks
164CLASS_1_80: Final[Element] = Element(
165 "class 1", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
166#: the class 1-100 benchmarks
167CLASS_1_100: Final[Element] = Element(
168 "class 1", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
170#: the class 2-20 benchmarks
171CLASS_2_20: Final[Element] = Element(
172 "class 2", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
173#: the class 2-40 benchmarks
174CLASS_2_40: Final[Element] = Element(
175 "class 2", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
176#: the class 2-60 benchmarks
177CLASS_2_60: Final[Element] = Element(
178 "class 2", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
179#: the class 2-80 benchmarks
180CLASS_2_80: Final[Element] = Element(
181 "class 2", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
182#: the class 1-100 benchmarks
183CLASS_2_100: Final[Element] = Element(
184 "class 2", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
186#: the class 3-20 benchmarks
187CLASS_3_20: Final[Element] = Element(
188 "class 3", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
189#: the class 1-40 benchmarks
190CLASS_3_40: Final[Element] = Element(
191 "class 3", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
192#: the class 3-60 benchmarks
193CLASS_3_60: Final[Element] = Element(
194 "class 3", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
195#: the class 3-80 benchmarks
196CLASS_3_80: Final[Element] = Element(
197 "class 3", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
198#: the class 3-100 benchmarks
199CLASS_3_100: Final[Element] = Element(
200 "class 3", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
202#: the class 4-20 benchmarks
203CLASS_4_20: Final[Element] = Element(
204 "class 4", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
205#: the class 4-40 benchmarks
206CLASS_4_40: Final[Element] = Element(
207 "class 4", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
208#: the class 4-60 benchmarks
209CLASS_4_60: Final[Element] = Element(
210 "class 4", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
211#: the class 4-80 benchmarks
212CLASS_4_80: Final[Element] = Element(
213 "class 4", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
214#: the class 4-100 benchmarks
215CLASS_4_100: Final[Element] = Element(
216 "class 4", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
218#: the class 5-20 benchmarks
219CLASS_5_20: Final[Element] = Element(
220 "class 5", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
221#: the class 5-40 benchmarks
222CLASS_5_40: Final[Element] = Element(
223 "class 5", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
224#: the class 5-60 benchmarks
225CLASS_5_60: Final[Element] = Element(
226 "class 5", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
227#: the class 5-80 benchmarks
228CLASS_5_80: Final[Element] = Element(
229 "class 5", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
230#: the class 5-100 benchmarks
231CLASS_5_100: Final[Element] = Element(
232 "class 5", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
234#: the class 6-20 benchmarks
235CLASS_6_20: Final[Element] = Element(
236 "class 6", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
237#: the class 6-40 benchmarks
238CLASS_6_40: Final[Element] = Element(
239 "class 6", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
240#: the class 6-60 benchmarks
241CLASS_6_60: Final[Element] = Element(
242 "class 6", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
243#: the class 6-80 benchmarks
244CLASS_6_80: Final[Element] = Element(
245 "class 6", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
246#: the class 6-100 benchmarks
247CLASS_6_100: Final[Element] = Element(
248 "class 6", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
250#: the class 7-20 benchmarks
251CLASS_7_20: Final[Element] = Element(
252 "class 7", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
253#: the class 7-40 benchmarks
254CLASS_7_40: Final[Element] = Element(
255 "class 7", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
256#: the class 7-60 benchmarks
257CLASS_7_60: Final[Element] = Element(
258 "class 7", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
259#: the class 7-80 benchmarks
260CLASS_7_80: Final[Element] = Element(
261 "class 7", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
262#: the class 7-100 benchmarks
263CLASS_7_100: Final[Element] = Element(
264 "class 7", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
266#: the class 8-20 benchmarks
267CLASS_8_20: Final[Element] = Element(
268 "class 8", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
269#: the class 8-40 benchmarks
270CLASS_8_40: Final[Element] = Element(
271 "class 8", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
272#: the class 8-60 benchmarks
273CLASS_8_60: Final[Element] = Element(
274 "class 8", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
275#: the class 8-80 benchmarks
276CLASS_8_80: Final[Element] = Element(
277 "class 8", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
278#: the class 8-100 benchmarks
279CLASS_8_100: Final[Element] = Element(
280 "class 8", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
282#: the class 9-20 benchmarks
283CLASS_9_20: Final[Element] = Element(
284 "class 9", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
285#: the class 9-40 benchmarks
286CLASS_9_40: Final[Element] = Element(
287 "class 9", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
288#: the class 9-60 benchmarks
289CLASS_9_60: Final[Element] = Element(
290 "class 9", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
291#: the class 9-80 benchmarks
292CLASS_9_80: Final[Element] = Element(
293 "class 9", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
294#: the class 9-100 benchmarks
295CLASS_9_100: Final[Element] = Element(
296 "class 9", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
298#: the class 10-20 benchmarks
299CLASS_10_20: Final[Element] = Element(
300 "class 10", "20", "BW1987TDFBPA,MV1998ESOTTDFBPP")
301#: the class 10-40 benchmarks
302CLASS_10_40: Final[Element] = Element(
303 "class 10", "40", "BW1987TDFBPA,MV1998ESOTTDFBPP")
304#: the class 10-60 benchmarks
305CLASS_10_60: Final[Element] = Element(
306 "class 10", "60", "BW1987TDFBPA,MV1998ESOTTDFBPP")
307#: the class 10-80 benchmarks
308CLASS_10_80: Final[Element] = Element(
309 "class 10", "80", "BW1987TDFBPA,MV1998ESOTTDFBPP")
310#: the class 6-100 benchmarks
311CLASS_10_100: Final[Element] = Element(
312 "class 10", "100", "BW1987TDFBPA,MV1998ESOTTDFBPP")
315def __sort_key(txt: str) -> list[str]:
316 """
317 Get a sort key from a string.
319 :param txt: the string
320 :return: the sort key
321 """
322 kys: list[str] = []
323 for v in txt.split(","):
324 for vv in v.split("-"):
325 for vvv in vv.split("_"):
326 for vvvv in vvv.split(" "):
327 if vvvv == "small":
328 kys.append("1_small")
329 elif vvvv == "med":
330 kys.append("2_med")
331 elif vvvv == "large":
332 kys.append("3_large")
333 elif vvvv.startswith("asqas"):
334 kys.append(f"zzzzzzzzzzzzzzzzzz{vvvv}")
335 else:
336 kys.append(vvvv.rjust(10, "0")
337 if vvvv.isdigit() else vvvv)
338 return kys
341#: The set of class and beng instance groups
342CLASS_AND_BENG: Final[tuple[Element, ...]] = (
343 BENG_1_8, BENG_9_10, CLASS_1_20, CLASS_1_40, CLASS_1_60, CLASS_1_80,
344 CLASS_1_100, CLASS_2_20, CLASS_2_40, CLASS_2_60, CLASS_2_80,
345 CLASS_2_100, CLASS_3_20, CLASS_3_40, CLASS_3_60, CLASS_3_80,
346 CLASS_3_100, CLASS_4_20, CLASS_4_40, CLASS_4_60, CLASS_4_80,
347 CLASS_4_100, CLASS_5_20, CLASS_5_40, CLASS_5_60, CLASS_5_80,
348 CLASS_5_100, CLASS_6_20, CLASS_6_40, CLASS_6_60, CLASS_6_80,
349 CLASS_6_100, CLASS_7_20, CLASS_7_40, CLASS_7_60, CLASS_7_80,
350 CLASS_7_100, CLASS_8_20, CLASS_8_40, CLASS_8_60, CLASS_8_80,
351 CLASS_8_100, CLASS_9_20, CLASS_9_40, CLASS_9_60, CLASS_9_80,
352 CLASS_9_100, CLASS_10_20, CLASS_10_40, CLASS_10_60, CLASS_10_80,
353 CLASS_10_100,
354)
357def __make_instance_map() -> Mapping[Element, tuple[str, ...]]:
358 """
359 Make an instance map.
361 :return: the instance map.
362 """
363 groups: Final[list[Element]] = [
364 A_SMALL, A_MED, A_LARGE, ASQAS,
365 BENG_1_8, BENG_9_10, CLASS_1_20, CLASS_1_40, CLASS_1_60, CLASS_1_80,
366 CLASS_1_100, CLASS_2_20, CLASS_2_40, CLASS_2_60, CLASS_2_80,
367 CLASS_2_100, CLASS_3_20, CLASS_3_40, CLASS_3_60, CLASS_3_80,
368 CLASS_3_100, CLASS_4_20, CLASS_4_40, CLASS_4_60, CLASS_4_80,
369 CLASS_4_100, CLASS_5_20, CLASS_5_40, CLASS_5_60, CLASS_5_80,
370 CLASS_5_100, CLASS_6_20, CLASS_6_40, CLASS_6_60, CLASS_6_80,
371 CLASS_6_100, CLASS_7_20, CLASS_7_40, CLASS_7_60, CLASS_7_80,
372 CLASS_7_100, CLASS_8_20, CLASS_8_40, CLASS_8_60, CLASS_8_80,
373 CLASS_8_100, CLASS_9_20, CLASS_9_40, CLASS_9_60, CLASS_9_80,
374 CLASS_9_100, CLASS_10_20, CLASS_10_40, CLASS_10_60, CLASS_10_80,
375 CLASS_10_100]
377 data: dict[Element, tuple[str, ...]] = {}
378 for group in Instance.list_resources_groups():
379 prefix: str = group[0]
380 suffix: str = "" if group[1] is None else group[1]
381 insts: tuple[str, ...] = group[2]
382 found: bool = False
383 for ggg in groups:
384 if (ggg.name == prefix) and (ggg.name_suffix == suffix):
385 if ggg in data:
386 raise ValueError(f"Encountered {ggg} twice?")
387 data[ggg] = insts
388 found = True
389 break
390 if not found:
391 raise ValueError(f"Did not find {group!r}.")
393 return immutable_mapping({k: data[k] for k in sorted(
394 data.keys(), key=lambda e: __sort_key(f"{e.name} {e.name_suffix}"))})
397#: a mapping of instance groups to instances
398GROUPS_TO_INSTANCES: Final[Mapping[Element, tuple[
399 str, ...]]] = __make_instance_map()
401#: A sort key function for instance groups
402INST_GROUP_SORT_KEY: Final[Callable[[Element], int]] \
403 = list(GROUPS_TO_INSTANCES.keys()).index
406def __make_instance_to_groups() -> Mapping[str, Element]:
407 """
408 Make a mapping of instances to groups.
410 :return: the mapping of instances to groups.
411 """
412 data: list[tuple[str, Element]] = [(
413 v, k) for k, vv in GROUPS_TO_INSTANCES.items() for v in vv]
414 data.sort(key=lambda x: __sort_key(x[0]))
415 return immutable_mapping(dict(data))
418#: a mapping of instances to instance groups
419INSTANCES_TO_GROUPS: Final[Mapping[str, Element]] = \
420 __make_instance_to_groups()
423def __lb_avg_denormalize(
424 with_rotation: bool, algo: Element, group: Element,
425 value: "int | float", min_result: int = -1_000_000) -> tuple[
426 bool, Element, Element, int]:
427 """
428 De-normalize using the maximum of the Dell'Amico and geometric bound.
430 :param with_rotation: is this a result with or without rotation?
431 :param algo: the algorithm used
432 :param group: the instance group
433 :param value: the value
434 :param min_result: the minimum denormalized result
435 :return: de-normalized average
436 """
437 return (with_rotation, algo, group, max(
438 min_result, round(try_int_mul(sum(Instance.from_resource(
439 ins).lower_bound_bins for ins in GROUPS_TO_INSTANCES[group]),
440 value))))
443#: the related works with rotation averaged
444__RW__AVERAGE: Final[tuple[tuple[
445 bool, Element, Element, int], ...]] = (
446 (True, BRKGA_BPP_2R, CLASS_1_20, 66),
447 (False, BRKGA_BPP_ANB, CLASS_1_20, 71),
448 (True, BRKGA_BPP_2R, CLASS_1_40, 128),
449 (False, BRKGA_BPP_ANB, CLASS_1_40, 134),
450 (True, BRKGA_BPP_2R, CLASS_1_60, 195),
451 (False, BRKGA_BPP_ANB, CLASS_1_60, 200),
452 (True, BRKGA_BPP_2R, CLASS_1_80, 270),
453 (False, BRKGA_BPP_ANB, CLASS_1_80, 275),
454 (True, BRKGA_BPP_2R, CLASS_1_100, 313),
455 (False, BRKGA_BPP_ANB, CLASS_1_100, 317),
456 (True, BRKGA_BPP_2R, CLASS_2_20, 10),
457 (False, BRKGA_BPP_ANB, CLASS_2_20, 10),
458 (True, BRKGA_BPP_2R, CLASS_2_40, 19),
459 (False, BRKGA_BPP_ANB, CLASS_2_40, 19),
460 (True, BRKGA_BPP_2R, CLASS_2_60, 25),
461 (False, BRKGA_BPP_ANB, CLASS_2_60, 25),
462 (True, BRKGA_BPP_2R, CLASS_2_80, 31),
463 (False, BRKGA_BPP_ANB, CLASS_2_80, 31),
464 (True, BRKGA_BPP_2R, CLASS_2_100, 39),
465 (False, BRKGA_BPP_ANB, CLASS_2_100, 39),
466 (True, BRKGA_BPP_2R, CLASS_3_20, 47),
467 (False, BRKGA_BPP_ANB, CLASS_3_20, 51),
468 (True, BRKGA_BPP_2R, CLASS_3_40, 92),
469 (False, BRKGA_BPP_ANB, CLASS_3_40, 94),
470 (True, BRKGA_BPP_2R, CLASS_3_60, 134),
471 (False, BRKGA_BPP_ANB, CLASS_3_60, 139),
472 (True, BRKGA_BPP_2R, CLASS_3_80, 182),
473 (False, BRKGA_BPP_ANB, CLASS_3_80, 189),
474 (True, BRKGA_BPP_2R, CLASS_3_100, 220),
475 (False, BRKGA_BPP_ANB, CLASS_3_100, 223),
476 (True, BRKGA_BPP_2R, CLASS_4_20, 10),
477 (False, BRKGA_BPP_ANB, CLASS_4_20, 10),
478 (True, BRKGA_BPP_2R, CLASS_4_40, 19),
479 (False, BRKGA_BPP_ANB, CLASS_4_40, 19),
480 (True, BRKGA_BPP_2R, CLASS_4_60, 23),
481 (False, BRKGA_BPP_ANB, CLASS_4_60, 25),
482 (True, BRKGA_BPP_2R, CLASS_4_80, 31),
483 (False, BRKGA_BPP_ANB, CLASS_4_80, 31),
484 (True, BRKGA_BPP_2R, CLASS_4_100, 37),
485 (False, BRKGA_BPP_ANB, CLASS_4_100, 37),
486 (True, BRKGA_BPP_2R, CLASS_5_20, 59),
487 (False, BRKGA_BPP_ANB, CLASS_5_20, 65),
488 (True, BRKGA_BPP_2R, CLASS_5_40, 114),
489 (False, BRKGA_BPP_ANB, CLASS_5_40, 119),
490 (True, BRKGA_BPP_2R, CLASS_5_60, 172),
491 (False, BRKGA_BPP_ANB, CLASS_5_60, 180),
492 (True, BRKGA_BPP_2R, CLASS_5_80, 239),
493 (False, BRKGA_BPP_ANB, CLASS_5_80, 247),
494 (True, BRKGA_BPP_2R, CLASS_5_100, 277),
495 (False, BRKGA_BPP_ANB, CLASS_5_100, 281),
496 (True, BRKGA_BPP_2R, CLASS_6_20, 10),
497 (False, BRKGA_BPP_ANB, CLASS_6_20, 10),
498 (True, BRKGA_BPP_2R, CLASS_6_40, 16),
499 (False, BRKGA_BPP_ANB, CLASS_6_40, 16),
500 (True, BRKGA_BPP_2R, CLASS_6_60, 21),
501 (False, BRKGA_BPP_ANB, CLASS_6_60, 21),
502 (True, BRKGA_BPP_2R, CLASS_6_80, 30),
503 (False, BRKGA_BPP_ANB, CLASS_6_80, 30),
504 (True, BRKGA_BPP_2R, CLASS_6_100, 32),
505 (False, BRKGA_BPP_ANB, CLASS_6_100, 33),
506 (True, BRKGA_BPP_2R, CLASS_7_20, 52),
507 (False, BRKGA_BPP_ANB, CLASS_7_20, 55),
508 (True, BRKGA_BPP_2R, CLASS_7_40, 102),
509 (False, BRKGA_BPP_ANB, CLASS_7_40, 111),
510 (True, BRKGA_BPP_2R, CLASS_7_60, 146),
511 (False, BRKGA_BPP_ANB, CLASS_7_60, 158),
512 (True, BRKGA_BPP_2R, CLASS_7_80, 208),
513 (False, BRKGA_BPP_ANB, CLASS_7_80, 232),
514 (True, BRKGA_BPP_2R, CLASS_7_100, 250),
515 (False, BRKGA_BPP_ANB, CLASS_7_100, 271),
516 (True, BRKGA_BPP_2R, CLASS_8_20, 53),
517 (False, BRKGA_BPP_ANB, CLASS_8_20, 58),
518 (True, BRKGA_BPP_2R, CLASS_8_40, 103),
519 (False, BRKGA_BPP_ANB, CLASS_8_40, 113),
520 (True, BRKGA_BPP_2R, CLASS_8_60, 147),
521 (False, BRKGA_BPP_ANB, CLASS_8_60, 161),
522 (True, BRKGA_BPP_2R, CLASS_8_80, 204),
523 (False, BRKGA_BPP_ANB, CLASS_8_80, 224),
524 (True, BRKGA_BPP_2R, CLASS_8_100, 252),
525 (False, BRKGA_BPP_ANB, CLASS_8_100, 278),
526 (True, BRKGA_BPP_2R, CLASS_9_20, 143),
527 (False, BRKGA_BPP_ANB, CLASS_9_20, 143),
528 (True, BRKGA_BPP_2R, CLASS_9_40, 275),
529 (False, BRKGA_BPP_ANB, CLASS_9_40, 278),
530 (True, BRKGA_BPP_2R, CLASS_9_60, 435),
531 (False, BRKGA_BPP_ANB, CLASS_9_60, 437),
532 (True, BRKGA_BPP_2R, CLASS_9_80, 573),
533 (False, BRKGA_BPP_ANB, CLASS_9_80, 577),
534 (True, BRKGA_BPP_2R, CLASS_9_100, 693),
535 (False, BRKGA_BPP_ANB, CLASS_9_100, 695),
536 (True, BRKGA_BPP_2R, CLASS_10_20, 41),
537 (False, BRKGA_BPP_ANB, CLASS_10_20, 42),
538 (True, BRKGA_BPP_2R, CLASS_10_40, 72),
539 (False, BRKGA_BPP_ANB, CLASS_10_40, 74),
540 (True, BRKGA_BPP_2R, CLASS_10_60, 99),
541 (False, BRKGA_BPP_ANB, CLASS_10_60, 100),
542 (True, BRKGA_BPP_2R, CLASS_10_80, 125),
543 (False, BRKGA_BPP_ANB, CLASS_10_80, 128),
544 (True, BRKGA_BPP_2R, CLASS_10_100, 154),
545 (False, BRKGA_BPP_ANB, CLASS_10_100, 158),
546 (True, BRKGA_BPP_2R, BENG_1_8, 54),
547 (False, BRKGA_BPP_ANB, BENG_1_8, 54),
548 (True, BRKGA_BPP_2R, BENG_9_10, 13),
549 (False, BRKGA_BPP_ANB, BENG_9_10, 13),
550 (False, PAC, CLASS_1_20, 71),
551 (False, PAC, CLASS_1_40, 134),
552 (False, PAC, CLASS_1_60, 200),
553 (False, PAC, CLASS_1_80, 275),
554 (False, PAC, CLASS_1_100, 317),
555 (False, PAC, CLASS_2_20, 10),
556 (False, PAC, CLASS_2_40, 19),
557 (False, PAC, CLASS_2_60, 25),
558 (False, PAC, CLASS_2_80, 31),
559 (False, PAC, CLASS_2_100, 39),
560 (False, PAC, CLASS_3_20, 51),
561 (False, PAC, BENG_1_8, 54),
562 (False, PAC, BENG_9_10, 13),
563 (True, PAC, CLASS_1_20, 66),
564 (True, PAC, CLASS_1_40, 128),
565 (True, PAC, CLASS_1_60, 195),
566 (True, PAC, CLASS_1_80, 270),
567 (True, PAC, CLASS_1_100, 313),
568 (True, PAC, CLASS_2_20, 10),
569 (True, PAC, CLASS_2_40, 19),
570 (True, PAC, CLASS_2_60, 25),
571 (True, PAC, CLASS_2_80, 31),
572 (True, PAC, CLASS_2_100, 39),
573 (True, PAC, CLASS_3_20, 47),
574 (True, PAC, BENG_1_8, 54),
575 (True, PAC, BENG_9_10, 13),
576 (True, HHANO_R, CLASS_1_20, 66),
577 (True, HHANO_SR, CLASS_1_20, 66),
578 (True, HHANO_R, CLASS_1_40, 131),
579 (True, HHANO_SR, CLASS_1_40, 129),
580 (True, HHANO_R, CLASS_1_60, 196),
581 (True, HHANO_SR, CLASS_1_60, 195),
582 (True, HHANO_R, CLASS_1_80, 270),
583 (True, HHANO_SR, CLASS_1_80, 270),
584 (True, HHANO_R, CLASS_1_100, 314),
585 (True, HHANO_SR, CLASS_1_100, 313),
586 (True, HHANO_R, CLASS_2_20, 10),
587 (True, HHANO_SR, CLASS_2_20, 10),
588 (True, HHANO_R, CLASS_2_40, 20),
589 (True, HHANO_SR, CLASS_2_40, 19),
590 (True, HHANO_R, CLASS_2_60, 25),
591 (True, HHANO_SR, CLASS_2_60, 25),
592 (True, HHANO_R, CLASS_2_80, 31),
593 (True, HHANO_SR, CLASS_2_80, 31),
594 (True, HHANO_R, CLASS_2_100, 39),
595 (True, HHANO_SR, CLASS_2_100, 39),
596 (True, HHANO_R, CLASS_3_20, 48),
597 (True, HHANO_SR, CLASS_3_20, 48),
598 (True, HHANO_R, CLASS_3_40, 95),
599 (True, HHANO_SR, CLASS_3_40, 95),
600 (True, HHANO_R, CLASS_3_60, 137),
601 (True, HHANO_SR, CLASS_3_60, 137),
602 (True, HHANO_R, CLASS_3_80, 186),
603 (True, HHANO_SR, CLASS_3_80, 187),
604 (True, HHANO_R, CLASS_3_100, 225),
605 (True, HHANO_SR, CLASS_3_100, 225),
606 (True, HHANO_R, CLASS_4_20, 10),
607 (True, HHANO_SR, CLASS_4_20, 10),
608 (True, HHANO_R, CLASS_4_40, 19),
609 (True, HHANO_SR, CLASS_4_40, 19),
610 (True, HHANO_R, CLASS_4_60, 25),
611 (True, HHANO_SR, CLASS_4_60, 25),
612 (True, HHANO_R, CLASS_4_80, 32),
613 (True, HHANO_SR, CLASS_4_80, 33),
614 (True, HHANO_R, CLASS_4_100, 38),
615 (True, HHANO_SR, CLASS_4_100, 38),
616 (True, HHANO_R, CLASS_5_20, 59),
617 (True, HHANO_SR, CLASS_5_20, 59),
618 (True, HHANO_R, CLASS_5_40, 116),
619 (True, HHANO_SR, CLASS_5_40, 115),
620 (True, HHANO_R, CLASS_5_60, 175),
621 (True, HHANO_SR, CLASS_5_60, 176),
622 (True, HHANO_R, CLASS_5_80, 240),
623 (True, HHANO_SR, CLASS_5_80, 241),
624 (True, HHANO_R, CLASS_5_100, 284),
625 (True, HHANO_SR, CLASS_5_100, 284),
626 (True, HHANO_R, CLASS_6_20, 10),
627 (True, HHANO_SR, CLASS_6_20, 10),
628 (True, HHANO_R, CLASS_6_40, 18),
629 (True, HHANO_SR, CLASS_6_40, 17),
630 (True, HHANO_R, CLASS_6_60, 22),
631 (True, HHANO_SR, CLASS_6_60, 22),
632 (True, HHANO_R, CLASS_6_80, 30),
633 (True, HHANO_SR, CLASS_6_80, 30),
634 (True, HHANO_R, CLASS_6_100, 34),
635 (True, HHANO_SR, CLASS_6_100, 34),
636 (True, HHANO_R, CLASS_7_20, 52),
637 (True, HHANO_SR, CLASS_7_20, 52),
638 (True, HHANO_R, CLASS_7_40, 106),
639 (True, HHANO_SR, CLASS_7_40, 107),
640 (True, HHANO_R, CLASS_7_60, 152),
641 (True, HHANO_SR, CLASS_7_60, 153),
642 (True, HHANO_R, CLASS_7_80, 216),
643 (True, HHANO_SR, CLASS_7_80, 217),
644 (True, HHANO_R, CLASS_7_100, 260),
645 (True, HHANO_SR, CLASS_7_100, 259),
646 (True, HHANO_R, CLASS_8_20, 53),
647 (True, HHANO_SR, CLASS_8_20, 53),
648 (True, HHANO_R, CLASS_8_40, 106),
649 (True, HHANO_SR, CLASS_8_40, 105),
650 (True, HHANO_R, CLASS_8_60, 155),
651 (True, HHANO_SR, CLASS_8_60, 154),
652 (True, HHANO_R, CLASS_8_80, 213),
653 (True, HHANO_SR, CLASS_8_80, 214),
654 (True, HHANO_R, CLASS_8_100, 261),
655 (True, HHANO_SR, CLASS_8_100, 262),
656 (True, HHANO_R, CLASS_9_20, 143),
657 (True, HHANO_SR, CLASS_9_20, 143),
658 (True, HHANO_R, CLASS_9_40, 275),
659 (True, HHANO_SR, CLASS_9_40, 275),
660 (True, HHANO_R, CLASS_9_60, 435),
661 (True, HHANO_SR, CLASS_9_60, 435),
662 (True, HHANO_R, CLASS_9_80, 573),
663 (True, HHANO_SR, CLASS_9_80, 573),
664 (True, HHANO_R, CLASS_9_100, 693),
665 (True, HHANO_SR, CLASS_9_100, 693),
666 (True, HHANO_R, CLASS_10_20, 41),
667 (True, HHANO_SR, CLASS_10_20, 41),
668 (True, HHANO_R, CLASS_10_40, 73),
669 (True, HHANO_SR, CLASS_10_40, 73),
670 (True, HHANO_R, CLASS_10_60, 101),
671 (True, HHANO_SR, CLASS_10_60, 101),
672 (True, HHANO_R, CLASS_10_80, 129),
673 (True, HHANO_SR, CLASS_10_80, 130),
674 (True, HHANO_R, CLASS_10_100, 161),
675 (True, HHANO_SR, CLASS_10_100, 162),
676 __lb_avg_denormalize(True, MXGA, CLASS_1_20, 1.03),
677 __lb_avg_denormalize(True, MXGA, CLASS_1_40, 1.04),
678 __lb_avg_denormalize(True, MXGA, CLASS_1_60, 1.04, 195), # 19.4
679 __lb_avg_denormalize(True, MXGA, CLASS_1_80, 1.06),
680 __lb_avg_denormalize(True, MXGA, CLASS_1_100, 1.02, 313), # 31.2
681 __lb_avg_denormalize(True, MXGA, CLASS_2_20, 1.0),
682 __lb_avg_denormalize(True, MXGA, CLASS_2_40, 1.1),
683 __lb_avg_denormalize(True, MXGA, CLASS_2_60, 1.0),
684 __lb_avg_denormalize(True, MXGA, CLASS_2_80, 1.0),
685 __lb_avg_denormalize(True, MXGA, CLASS_2_100, 1.0),
686 __lb_avg_denormalize(True, MXGA, CLASS_3_20, 1.04),
687 __lb_avg_denormalize(True, MXGA, CLASS_3_40, 1.09),
688 __lb_avg_denormalize(True, MXGA, CLASS_3_60, 1.08),
689 __lb_avg_denormalize(True, MXGA, CLASS_3_80, 1.06),
690 __lb_avg_denormalize(True, MXGA, CLASS_3_100, 1.06),
691 __lb_avg_denormalize(True, MXGA, CLASS_4_20, 1.0),
692 __lb_avg_denormalize(True, MXGA, CLASS_4_40, 1.0),
693 __lb_avg_denormalize(True, MXGA, CLASS_4_60, 1.10),
694 __lb_avg_denormalize(True, MXGA, CLASS_4_80, 1.07),
695 __lb_avg_denormalize(True, MXGA, CLASS_4_100, 1.03),
696 __lb_avg_denormalize(True, MXGA, CLASS_5_20, 1.04),
697 __lb_avg_denormalize(True, MXGA, CLASS_5_40, 1.06),
698 __lb_avg_denormalize(True, MXGA, CLASS_5_60, 1.07),
699 __lb_avg_denormalize(True, MXGA, CLASS_5_80, 1.07),
700 __lb_avg_denormalize(True, MXGA, CLASS_5_100, 1.05),
701 __lb_avg_denormalize(True, MXGA, CLASS_6_20, 1.0),
702 __lb_avg_denormalize(True, MXGA, CLASS_6_40, 1.4),
703 __lb_avg_denormalize(True, MXGA, CLASS_6_60, 1.0),
704 __lb_avg_denormalize(True, MXGA, CLASS_6_80, 1.0),
705 __lb_avg_denormalize(True, MXGA, CLASS_6_100, 1.07),
706 __lb_avg_denormalize(True, MXGA, CLASS_7_20, 1.11),
707 __lb_avg_denormalize(True, MXGA, CLASS_7_40, 1.07),
708 __lb_avg_denormalize(True, MXGA, CLASS_7_60, 1.05),
709 __lb_avg_denormalize(True, MXGA, CLASS_7_80, 1.08),
710 __lb_avg_denormalize(True, MXGA, CLASS_7_100, 1.07),
711 __lb_avg_denormalize(True, MXGA, CLASS_8_20, 1.10),
712 __lb_avg_denormalize(True, MXGA, CLASS_8_40, 1.09),
713 __lb_avg_denormalize(True, MXGA, CLASS_8_60, 1.06),
714 __lb_avg_denormalize(True, MXGA, CLASS_8_80, 1.07),
715 __lb_avg_denormalize(True, MXGA, CLASS_8_100, 1.06),
716 __lb_avg_denormalize(True, MXGA, CLASS_9_20, 1.0),
717 __lb_avg_denormalize(True, MXGA, CLASS_9_40, 1.01),
718 __lb_avg_denormalize(True, MXGA, CLASS_9_60, 1.01),
719 __lb_avg_denormalize(True, MXGA, CLASS_9_80, 1.01),
720 __lb_avg_denormalize(True, MXGA, CLASS_9_100, 1.01),
721 __lb_avg_denormalize(True, MXGA, CLASS_10_20, 1.13),
722 __lb_avg_denormalize(True, MXGA, CLASS_10_40, 1.06),
723 __lb_avg_denormalize(True, MXGA, CLASS_10_60, 1.07),
724 __lb_avg_denormalize(True, MXGA, CLASS_10_80, 1.06),
725 __lb_avg_denormalize(True, MXGA, CLASS_10_100, 1.04),
726 (False, EALGFI, CLASS_1_20, 71),
727 (False, EALGFI, CLASS_1_40, 134),
728 (False, EALGFI, CLASS_1_60, 200),
729 (False, EALGFI, CLASS_1_80, 275),
730 (False, EALGFI, CLASS_1_100, 317),
731 (False, EALGFI, CLASS_2_20, 10),
732 (False, EALGFI, CLASS_2_40, 19),
733 (False, EALGFI, CLASS_2_60, 25),
734 (False, EALGFI, CLASS_2_80, 31),
735 (False, EALGFI, CLASS_2_100, 39),
736 (False, EALGFI, CLASS_3_20, 51),
737 (False, EALGFI, CLASS_3_40, 94),
738 (False, EALGFI, CLASS_3_60, 139),
739 (False, EALGFI, CLASS_3_80, 189),
740 (False, EALGFI, CLASS_3_100, 224),
741 (False, EALGFI, CLASS_4_20, 10),
742 (False, EALGFI, CLASS_4_40, 19),
743 (False, EALGFI, CLASS_4_60, 23),
744 (False, EALGFI, CLASS_4_80, 31),
745 (False, EALGFI, CLASS_4_100, 37),
746 (False, EALGFI, CLASS_5_20, 65),
747 (False, EALGFI, CLASS_5_40, 119),
748 (False, EALGFI, CLASS_5_60, 180),
749 (False, EALGFI, CLASS_5_80, 247),
750 (False, EALGFI, CLASS_5_100, 284),
751 (False, EALGFI, CLASS_6_20, 10),
752 (False, EALGFI, CLASS_6_40, 17),
753 (False, EALGFI, CLASS_6_60, 21),
754 (False, EALGFI, CLASS_6_80, 30),
755 (False, EALGFI, CLASS_6_100, 32),
756 (False, EALGFI, CLASS_7_20, 55),
757 (False, EALGFI, CLASS_7_40, 111),
758 (False, EALGFI, CLASS_7_60, 159),
759 (False, EALGFI, CLASS_7_80, 232),
760 (False, EALGFI, CLASS_7_100, 271),
761 (False, EALGFI, CLASS_8_20, 58),
762 (False, EALGFI, CLASS_8_40, 113),
763 (False, EALGFI, CLASS_8_60, 161),
764 (False, EALGFI, CLASS_8_80, 224),
765 (False, EALGFI, CLASS_8_100, 277),
766 (False, EALGFI, CLASS_9_20, 143),
767 (False, EALGFI, CLASS_9_40, 278),
768 (False, EALGFI, CLASS_9_60, 437),
769 (False, EALGFI, CLASS_9_80, 577),
770 (False, EALGFI, CLASS_9_100, 695),
771 (False, EALGFI, CLASS_10_20, 42),
772 (False, EALGFI, CLASS_10_40, 74),
773 (False, EALGFI, CLASS_10_60, 101),
774 (False, EALGFI, CLASS_10_80, 128),
775 (False, EALGFI, CLASS_10_100, 160),
776 (False, GRASP_VND, CLASS_1_20, 71),
777 (False, GRASP_VND, CLASS_1_40, 134),
778 (False, GRASP_VND, CLASS_1_60, 200),
779 (False, GRASP_VND, CLASS_1_80, 275),
780 (False, GRASP_VND, CLASS_1_100, 317),
781 (False, GRASP_VND, CLASS_2_20, 10),
782 (False, GRASP_VND, CLASS_2_40, 19),
783 (False, GRASP_VND, CLASS_2_60, 25),
784 (False, GRASP_VND, CLASS_2_80, 31),
785 (False, GRASP_VND, CLASS_2_100, 39),
786 (False, GRASP_VND, CLASS_3_20, 51),
787 (False, GRASP_VND, CLASS_3_40, 94),
788 (False, GRASP_VND, CLASS_3_60, 139),
789 (False, GRASP_VND, CLASS_3_80, 189),
790 (False, GRASP_VND, CLASS_3_100, 223),
791 (False, GRASP_VND, CLASS_4_20, 10),
792 (False, GRASP_VND, CLASS_4_40, 19),
793 (False, GRASP_VND, CLASS_4_60, 25),
794 (False, GRASP_VND, CLASS_4_80, 31),
795 (False, GRASP_VND, CLASS_4_100, 38),
796 (False, GRASP_VND, CLASS_5_20, 65),
797 (False, GRASP_VND, CLASS_5_40, 119),
798 (False, GRASP_VND, CLASS_5_60, 180),
799 (False, GRASP_VND, CLASS_5_80, 247),
800 (False, GRASP_VND, CLASS_5_100, 282),
801 (False, GRASP_VND, CLASS_6_20, 10),
802 (False, GRASP_VND, CLASS_6_40, 17),
803 (False, GRASP_VND, CLASS_6_60, 21),
804 (False, GRASP_VND, CLASS_6_80, 30),
805 (False, GRASP_VND, CLASS_6_100, 34),
806 (False, GRASP_VND, CLASS_7_20, 55),
807 (False, GRASP_VND, CLASS_7_40, 111),
808 (False, GRASP_VND, CLASS_7_60, 159),
809 (False, GRASP_VND, CLASS_7_80, 232),
810 (False, GRASP_VND, CLASS_7_100, 271),
811 (False, GRASP_VND, CLASS_8_20, 58),
812 (False, GRASP_VND, CLASS_8_40, 113),
813 (False, GRASP_VND, CLASS_8_60, 161),
814 (False, GRASP_VND, CLASS_8_80, 224),
815 (False, GRASP_VND, CLASS_8_100, 278),
816 (False, GRASP_VND, CLASS_9_20, 143),
817 (False, GRASP_VND, CLASS_9_40, 278),
818 (False, GRASP_VND, CLASS_9_60, 437),
819 (False, GRASP_VND, CLASS_9_80, 577),
820 (False, GRASP_VND, CLASS_9_100, 695),
821 (False, GRASP_VND, CLASS_10_20, 42),
822 (False, GRASP_VND, CLASS_10_40, 74),
823 (False, GRASP_VND, CLASS_10_60, 100),
824 (False, GRASP_VND, CLASS_10_80, 129),
825 (False, GRASP_VND, CLASS_10_100, 159),
826 (False, GRASP_VND, BENG_1_8, 54),
827 (False, GRASP_VND, BENG_9_10, 13),
828)
831def get_related_work(
832 with_rotation: bool | None = None,
833 without_rotation: bool | None = None,
834 algo_select: Callable[[Element], bool] =
835 lambda _: True,
836 inst_group_select: Callable[[Element], bool] =
837 lambda _: True) -> tuple[
838 tuple[bool, Element, Element, int], ...]:
839 """
840 Get the related work of a given type.
842 :param with_rotation: include the data with rotation
843 :param without_rotation: include the data without rotation
844 :param algo_select: the algorithm selector
845 :param inst_group_select: the instance group selector
846 :return: An iterable with the related works
847 """
848 res: Iterable[tuple[bool, Element, Element, int]]
849 if (with_rotation is None) and (without_rotation is None):
850 res = __RW__AVERAGE
851 else:
852 if with_rotation is None:
853 with_rotation = not without_rotation
854 if without_rotation is None:
855 without_rotation = not with_rotation
857 if without_rotation and with_rotation:
858 res = __RW__AVERAGE
859 elif with_rotation:
860 res = (x for x in __RW__AVERAGE if x[0])
861 elif without_rotation:
862 res = (x for x in __RW__AVERAGE if not x[0])
863 else:
864 return ()
865 return tuple(sorted(filter(
866 lambda x: algo_select(x[1]) and inst_group_select(x[2]), res),
867 key=lambda v: (v[0], INST_GROUP_SORT_KEY(v[2]), v[1], v[3])))
870def make_comparison_table_data(
871 data: Iterable[PackingResult],
872 with_rotation: bool,
873 algo_from_pr: Callable[[PackingResult], Element] = lambda x: Element(
874 x.end_result.algorithm, x.end_result.objective),
875 algo_sort_key: Callable[[Element], Any] = lambda x: (
876 -1 if (x.reference == NO_REF) else 1, x),
877 rw_algo_selector: Callable[[Element], bool] = lambda _: True,
878 aggregator: Callable[[Iterable[int | float]], int | float] = mean,
879 additional: Callable[[Element], Iterable[tuple[
880 Element, Callable[[Iterable[int | float]], int | float]]]] =
881 lambda _: ()) -> tuple[tuple[Element, ...], tuple[tuple[
882 Element, tuple[int | float | None, ...]], ...]]:
883 """
884 Create the data for an end result comparison table.
886 :param data: the source data
887 :param with_rotation: are we doing stuff with rotation?
888 :param algo_from_pr: convert a packing result to an algorithm
889 :param algo_sort_key: the algorithm sort key
890 :param rw_algo_selector: the related work algorithm selector
891 :param aggregator: the routine for per-instance averaging of bins
892 :param additional: an additional column constructor
894 :return: the table data: the title row columns followed by the data
895 row-by-row, each row leading with an instance group identifier
896 """
897 per_algo_data: dict[Element, tuple[Callable[[
898 Iterable[int | float]], int | float], dict[str, list[int]]]] = {}
899 for pr in data:
900 algo: Element = algo_from_pr(pr)
901 inst_dict: dict[str, list[int]]
902 if algo in per_algo_data:
903 inst_dict = per_algo_data[algo][1]
904 else:
905 inst_dict = {}
906 per_algo_data[algo] = aggregator, inst_dict
907 inst: str = pr.end_result.instance
908 val: int = check_int_range(
909 pr.objectives[BIN_COUNT_NAME], BIN_COUNT_NAME, 1, 1_000_000_000)
910 if inst in inst_dict:
911 inst_dict[inst].append(val)
912 else:
913 inst_dict[inst] = [val]
915 groups: Final[list[Element]] = sorted({
916 INSTANCES_TO_GROUPS[k] for kk in per_algo_data.values()
917 for k in kk[1]}, key=INST_GROUP_SORT_KEY)
919 # get the averages
920 algo_per_group_data: Final[dict[Element, dict[Element, int | float]]] = {}
921 for algo, inst_dict_and_avg in per_algo_data.items():
922 dothis: list[tuple[Element, Callable[[
923 Iterable[int | float]], int | float]]] = [
924 (algo, inst_dict_and_avg[0])]
925 dothis.extend(additional(algo))
927 for xyz in dothis:
928 avg: Callable[[Iterable[int | float]], int | float] = xyz[1]
929 inst_dict = inst_dict_and_avg[1]
930 ag: dict[Element, int | float] = {}
931 algo_per_group_data[xyz[0]] = ag
932 algo_runs: int | None = None
933 for g in groups:
934 gis: tuple[str, ...] = GROUPS_TO_INSTANCES[g]
935 gisd: list[list[int | float]] = []
936 ok: bool = True
937 for inst in gis:
938 if inst not in inst_dict:
939 ok = False
940 gisd.append(cast("list[int | float]", inst_dict[inst]))
941 if ok:
942 if list.__len__(gisd) <= 0:
943 raise ValueError(f"Empty group {g!r} for {algo!r}?")
944 if list.__len__(gisd) != tuple.__len__(gis):
945 raise ValueError(
946 f"Some data missing in {g!r} for {algo!r}?")
947 ll = list.__len__(gisd[0])
948 if algo_runs is None:
949 algo_runs = ll
950 elif algo_runs != ll:
951 raise ValueError(
952 f"Inconsistent number of runs for {algo!r}: "
953 f"{ll} in {gis[0]!r} vs. {algo_runs}.")
954 if not all(list.__len__(xxx) == ll for xxx in gisd):
955 raise ValueError(
956 f"Inconsistent run numbers in {g!r} for {algo!r}.")
957 ag[g] = round(sum(cast("Iterable", (
958 try_int(avg(v)) for v in gisd))))
959 elif len(gisd) > 0:
960 raise ValueError(f"Incomplete group {g!r} for {algo!r}.")
962 rw: tuple[tuple[bool, Element, Element, float], ...] = get_related_work(
963 with_rotation=with_rotation, without_rotation=not with_rotation,
964 algo_select=rw_algo_selector, inst_group_select=groups.__contains__)
966 algorithms: list[Element] = list({rww[1] for rww in rw})
967 algorithms.sort(key=algo_sort_key)
968 our_algorithms = sorted(algo_per_group_data.keys(), key=algo_sort_key)
970 rows: list[tuple[Element, tuple[int | float | None, ...]]] = []
971 for g in groups:
972 xdata: list[int | float | None] = []
973 for algo in algorithms:
974 found: bool = False
975 for zz in rw:
976 if zz[1] != algo:
977 continue
978 if zz[2] != g:
979 continue
980 found = True
981 xdata.append(zz[3])
982 break
983 if found:
984 continue
985 xdata.append(None)
986 for algo in our_algorithms:
987 agz: dict[Element, int | float] = algo_per_group_data[algo]
988 if g not in agz:
989 xdata.append(None)
990 continue
991 xdata.append(agz[g])
992 rows.append((g, tuple(xdata)))
994 algorithms.extend(our_algorithms)
995 return tuple(algorithms), tuple(rows)
998def make_comparison_table(
999 dest: Callable[[str], Any],
1000 data: tuple[tuple[Element, ...], tuple[tuple[Element, tuple[
1001 int | float | None, ...]], ...]],
1002 name_to_strs: Callable[[Element], tuple[str, str]] = lambda s: (
1003 s.name.split("_")[0], s.name_suffix),
1004 format_best: Callable[[str], str] =
1005 lambda s: f"\\textbf{{{s}}}",
1006 count_best_over: Iterable[tuple[Iterable[Element], str]] = ()) -> None:
1007 """
1008 Make a comparison table in LaTeX.
1010 :param dest: the destination
1011 :param data: the source data
1012 :param name_to_strs: the converter of names to strings
1013 :param format_best: the formatter for best values
1014 :param count_best_over: a set of instances to include in the best counting
1015 and a corresponding label
1016 """
1017 inst_names: Final[list[tuple[str, str]]] = [
1018 name_to_strs(d[0]) for d in data[1]]
1019 inst_cols: int = 2 if any(
1020 str.__len__(n[1]) > 0 for n in inst_names) else 1
1022 dest("% begin auto-generated LaTeX table comparing "
1023 "end results with related work.%")
1024 writer: list[str] = [r"\begin{tabular}{@{}r"]
1025 if inst_cols > 1:
1026 writer.append("@{}l")
1027 writer.extend("r" for _ in range(tuple.__len__(data[0])))
1028 writer.append("@{}}%")
1029 dest("".join(writer))
1030 writer.clear()
1031 dest(r"\hline%")
1033 head_names: Final[list[tuple[str, str]]] = [
1034 name_to_strs(d) for d in data[0]]
1035 head_names.insert(0, ("instance", "group"))
1036 if inst_cols > 1:
1037 head_names.insert(0, ("instance", "group"))
1038 head_count: Final[int] = list.__len__(head_names)
1040 for dim in (0, 1):
1041 i = 0
1042 while i < head_count:
1043 head = head_names[i]
1044 next_i = i + 1
1045 while (next_i < head_count) and (
1046 head_names[next_i][dim] == head[dim]):
1047 next_i += 1
1049 col_txt: str = f"\\multicolumn{{{next_i - i}}}{{"
1050 if i <= 0:
1051 col_txt = f"{col_txt}@{{}}"
1052 col_txt = f"{col_txt}c"
1053 if next_i >= head_count:
1054 col_txt = f"{col_txt}@{{}}"
1055 col_txt = f"{col_txt}}}{{{head[dim]}}}"
1056 if next_i >= head_count:
1057 col_txt = f"{col_txt}\\\\%"
1058 writer.append(col_txt)
1059 i = next_i
1060 dest("&".join(writer))
1061 writer.clear()
1062 last_first_name = ""
1064 count_best: list[tuple[str, list[int]]] = []
1065 count_as: dict[Element, list[list[int]]] = {}
1066 for count_this, title in count_best_over:
1067 lst: list[int] = [0] * tuple.__len__(data[0])
1068 count_best.append((title, lst))
1069 for k in count_this:
1070 if k in count_as:
1071 count_as[k].append(lst)
1072 else:
1073 count_as[k] = [lst]
1075 for i, data_row in enumerate(data[1]):
1076 inst_name: tuple[str, str] = inst_names[i]
1077 if inst_name[0] != last_first_name:
1078 last_first_name = inst_name[0]
1079 dest(r"\hline%")
1080 writer.append(last_first_name)
1081 if inst_cols > 1:
1082 in1: str = str.strip(inst_name[1])
1083 if str.__len__(in1) > 0:
1084 in1 = str.strip(f"/{in1}")
1085 if str.__len__(in1) <= 1:
1086 in1 = ""
1087 writer.append(in1)
1089 row_data: tuple[int | float | None, ...] = data_row[1]
1090 minimum: int | float = inf
1091 for ddd in row_data:
1092 if (ddd is not None) and (ddd < minimum):
1093 minimum = ddd
1094 if not isfinite(minimum):
1095 raise ValueError(f"Huuhhh? {inst_name} has non-finite min?")
1096 instance: Element = data_row[0]
1097 for iij, ddd in enumerate(row_data):
1098 if ddd is None:
1099 writer.append("")
1100 continue
1101 writer.append(
1102 format_best(str(ddd)) if ddd <= minimum else str(ddd))
1103 if (ddd <= minimum) and (instance in count_as):
1104 for lst in count_as[instance]:
1105 lst[iij] += 1
1106 dest(f"{'&'.join(writer)}\\\\%")
1107 writer.clear()
1109 dest(r"\hline%")
1110 if list.__len__(count_best) > 0:
1111 for brow in count_best:
1112 writer.append(
1113 f"\\multicolumn{{{inst_cols}}}{{@{{}}r}}{{{brow[0]}}}")
1114 writer.extend(map(str, brow[1]))
1115 writer[-1] = f"{writer[-1]}\\\\%"
1116 dest("&".join(writer))
1117 dest(r"\hline%")
1118 dest(r"\end{tabular}%")
1119 dest("% end auto-generated LaTeX table comparing "
1120 "end results with related work.%")