Skip to content

Commit

Permalink
added 2dbpp instance generation code developed with Zhao Rui
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasWeise committed Oct 17, 2024
1 parent c4c796d commit 32454d4
Show file tree
Hide file tree
Showing 34 changed files with 1,664 additions and 52 deletions.
2 changes: 1 addition & 1 deletion moptipyapps/binpacking2d/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
(赵睿), <[email protected]> a Master's student at the Institute of Applied
Optimization (应用优化研究所, http://iao.hfuu.edu.cn) of the School of
Artificial Intelligence and Big Data (人工智能与大数据学院) at Hefei University
(合肥学院) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
(合肥大学) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
Prof. Dr. Thomas Weise (汤卫思教授).
"""
2 changes: 1 addition & 1 deletion moptipyapps/binpacking2d/encodings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
(赵睿), <[email protected]> a Master's student at the Institute of Applied
Optimization (应用优化研究所, http://iao.hfuu.edu.cn) of the School of
Artificial Intelligence and Big Data (人工智能与大数据学院) at Hefei University
(合肥学院) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
(合肥大学) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
Prof. Dr. Thomas Weise (汤卫思教授).
"""
2 changes: 1 addition & 1 deletion moptipyapps/binpacking2d/encodings/ibl_encoding_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
(赵睿), <[email protected]> a Master's student at the Institute of Applied
Optimization (应用优化研究所, http://iao.hfuu.edu.cn) of the School of
Artificial Intelligence and Big Data (人工智能与大数据学院) at Hefei University
(合肥学院) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
(合肥大学) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
Prof. Dr. Thomas Weise (汤卫思教授).
1. Dequan Liu and Hongfei Teng. An Improved BL-Algorithm for Genetic Algorithm
Expand Down
2 changes: 1 addition & 1 deletion moptipyapps/binpacking2d/encodings/ibl_encoding_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
(赵睿), <[email protected]>, a Master's student at the Institute of Applied
Optimization (应用优化研究所, http://iao.hfuu.edu.cn) of the School of
Artificial Intelligence and Big Data (人工智能与大数据学院) at Hefei University
(合肥学院) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
(合肥大学) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
Prof. Dr. Thomas Weise (汤卫思教授).
1. Dequan Liu and Hongfei Teng. An Improved BL-Algorithm for Genetic Algorithm
Expand Down
8 changes: 4 additions & 4 deletions moptipyapps/binpacking2d/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<[email protected]> a Master's student at the Institute of Applied
Optimization (应用优化研究所, http://iao.hfuu.edu.cn) of the School of
Artificial Intelligence and Big Data (人工智能与大数据学院) at Hefei University
(合肥学院) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
(合肥大学) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
Prof. Dr. Thomas Weise (汤卫思教授).
1. Manuel Iori, Vinícius Loti de Lima, Silvano Martello, and Michele Monaci.
Expand Down Expand Up @@ -594,7 +594,7 @@ def __new__(cls, name: str,
repetitions = check_int_range(int(repetitions), "repetitions",
1, 100_000_000)
item_area += (width * height * repetitions)
max_size = max(width, height)
max_size = max(max_size, width, height)
if (width > min_dim) and (height > min_dim):
raise ValueError(
f"object with width={width} and height={height} does "
Expand All @@ -604,8 +604,8 @@ def __new__(cls, name: str,

obj: Final[Instance] = super().__new__(
cls, use_shape, int_range_to_dtype(
min_value=0, max_value=max(max_dim + max_size, n_items),
force_signed=True))
min_value=0, max_value=max(
max_dim + max_size + 1, n_items + 1), force_signed=True))
for i in range(n_different_items):
obj[i, :] = matrix[i]

Expand Down
21 changes: 21 additions & 0 deletions moptipyapps/binpacking2d/instgen/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
Tools for generating 2d bin packing instances.
We want to generate instances of the two-dimensional bin packing problem.
These instances should have some pre-defined characteristics, e.g., width and
height of the bins, number of items to pack, lower bound/optimal number of
bins required by any solution, and so on.
At the same time, the instances should be hard.
We treat this whole thing as an optimization problem. Here, given are the
pre-defined instance characteristics and the goal is to find instances that
are hard to solve.
Important work on this code has been contributed by Mr. Rui ZHAO
(赵睿), <[email protected]> a Master's student at the Institute of Applied
Optimization (应用优化研究所, http://iao.hfuu.edu.cn) of the School of
Artificial Intelligence and Big Data (人工智能与大数据学院) at Hefei University
(合肥大学) in Hefei, Anhui, China (中国安徽省合肥市) under the supervision of
Prof. Dr. Thomas Weise (汤卫思教授).
"""
229 changes: 229 additions & 0 deletions moptipyapps/binpacking2d/instgen/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
"""
An objective function counting deviations from the instance definition.
This objective function will be the smaller, the closer the structure
of an instance is to the original instance.
Due to our encoding, we create instances whose bin width, bin height,
and the number of items is the same as in an existing instance. The
lower bound for the required number of bins is also the same.
This objective function here also incorporates some additional features, such
as:
1. Is the number of different items similar to those in the original instance?
In an existing instance, some items of same width and height could be
grouped together. We may have 10 items to pack, but only 3 different item
sizes exist. We here compare the number of different item sizes of a
generated instance with the number in the instance definition.
2. In a given instance, we can observe the minimum and maximum width and
height of any item. If an item in the generated instance is smaller than
the minimum or larger than the maximum permitted value in one dimension,
this will increase the error count.
3. Additionally, we want the actual minimum and maximum width and height of
any item in the generated instance is the same as in the original instance.
4. Finally, we want that the total area covered by all items is the same in
the generated instance as in the original instance.
All of these potential violations are counted and added up. Using this
objective function should drive the search towards generating instances that
are structurally similar to an existing instance, at least in some parameters.
We could push this arbitrarily further, trying to emulate the exact
distribution of the item sizes, etc. But this may just lead to the
reproduction of the original instance by the search and not add anything
interesting.
>>> orig = Instance.from_resource("a04")
>>> space = InstanceSpace(orig)
>>> print(f"{space.inst_name!r} with {space.n_different_items}/"
... f"{space.n_items} items with area {space.total_item_area} "
... f"in {space.min_bins} bins of "
... f"size {space.bin_width}*{space.bin_height}.")
'a04n' with 2/16 items with area 7305688 in 3 bins of size 2750*1220.
>>> from moptipyapps.binpacking2d.instgen.inst_decoding import InstanceDecoder
>>> decoder = InstanceDecoder(space)
>>> import numpy as np
>>> x = np.array([ 0.0, 0.2, -0.1, 0.3, 0.5, -0.6, -0.7, 0.9,
... 0.0, 0.2, -0.1, 0.3, 0.5, -0.6, -0.7, 0.9,
... 0.0, 0.2, -0.1, 0.3, 0.5, -0.6, -0.7, 0.9,
... 0.0, 0.2, ])
>>> y = space.create()
>>> decoder.decode(x, y)
>>> space.validate(y)
>>> res: Instance = y[0]
>>> print(f"{res.name!r} with {res.n_different_items}/"
... f"{res.n_items} items with area {res.total_item_area} "
... f"in {res.lower_bound_bins} bins of "
... f"size {res.bin_width}*{res.bin_height}.")
'a04n' with 15/16 items with area 10065000 in 3 bins of size 2750*1220.
>>> print(space.to_str(y))
a04n;15;2750;1220;1101,1098;2750,244;2750,98;1101,171;1649,171;2750,976;\
441,122;1649,122;2750,10;2750,1,2;2750,3;1649,1098;2750,878;2750,58;660,122
>>> errors = Errors(space)
>>> errors.lower_bound()
0
>>> errors.upper_bound()
7332043
>>> errors.evaluate(y)
2770589
>>> y[0] = orig
>>> errors.evaluate(y)
0
"""
from typing import Final

from moptipy.api.objective import Objective
from pycommons.types import type_error

from moptipyapps.binpacking2d.instance import (
IDX_HEIGHT,
IDX_REPETITION,
IDX_WIDTH,
Instance,
)
from moptipyapps.binpacking2d.instgen.instance_space import InstanceSpace


class Errors(Objective):
"""Compute the deviation of an instance from the definition."""

def __init__(self, space: InstanceSpace) -> None:
"""
Initialize the errors objective function.
:param instance: the instance to load the bounds from
"""
super().__init__()
if not isinstance(space, InstanceSpace):
raise type_error(space, "space", InstanceSpace)
#: the instance description
self.space: Final[InstanceSpace] = space

def evaluate(self, x: list[Instance] | Instance) -> int:
"""
Compute the deviations from the instance definition.
:param x: the instance
:return: the number of deviations
"""
errors: int = 0
inst: Final[Instance] = x[0] if isinstance(x, list) else x
space: Final[InstanceSpace] = self.space

# Some errors are not permitted.
errors += abs(inst.bin_width - space.bin_width) # should be 0!
errors += abs(inst.bin_height - space.bin_height) # should be 0!
errors += abs(inst.n_items - space.n_items) # should be 0!
if errors > 0:
raise ValueError(
f"Instance {inst} is inconsistent with space {space}.")

# These errors are permitted.
n_different: Final[int] = inst.n_different_items
errors += abs(n_different - space.n_different_items) # > 0
goal_min_width: Final[int] = space.item_width_min
goal_max_width: Final[int] = space.item_width_max
goal_min_height: Final[int] = space.item_height_min
goal_max_height: Final[int] = space.item_height_max

actual_min_width: int = space.bin_width
actual_max_width: int = 0
actual_min_height: int = space.bin_height
actual_max_height: int = 0
total_area: int = 0

for row in range(n_different):
width: int = int(inst[row, IDX_WIDTH])
height: int = int(inst[row, IDX_HEIGHT])
actual_min_width = min(actual_min_width, width)
actual_max_width = max(actual_max_width, width)
actual_min_height = min(actual_min_height, height)
actual_max_height = max(actual_max_height, height)

n: int = int(inst[row, IDX_REPETITION])
total_area += n * width * height
if width < goal_min_width:
errors += n * (goal_min_width - width)
elif width > goal_max_width:
errors += n * (width - goal_max_width)
if height < goal_min_height:
errors += n * (goal_min_height - height)
elif height > goal_max_height:
errors += n * (height - goal_max_height)

errors += abs(actual_min_width - goal_min_width)
errors += abs(actual_max_width - goal_max_width)
errors += abs(actual_min_height - goal_min_height)
errors += abs(actual_max_height - goal_max_height)

if total_area != inst.total_item_area:
raise ValueError("Invalid total area?")
errors += abs(total_area - space.total_item_area)

return errors

def lower_bound(self) -> int:
"""
Get the lower bound of the instance template deviations.
:return: the lower bound for the errors
"""
return 0

def is_always_integer(self) -> bool:
"""
Return `True` because there are only integer errors.
:retval True: always
"""
return True

def upper_bound(self) -> int:
"""
Get the upper bound of the number of deviations.
:return: the upper bound
"""
space: Final[InstanceSpace] = self.space

# These errors are permitted.
max_errors: int = max(
space.n_different_items - 1,
space.n_items - space.n_different_items - 1)

goal_min_width: Final[int] = space.item_width_min
goal_max_width: Final[int] = space.item_width_max
goal_min_height: Final[int] = space.item_height_min
goal_max_height: Final[int] = space.item_height_max
bin_width: Final[int] = space.bin_width
bin_height: Final[int] = space.bin_height

n_items: Final[int] = space.n_items
max_errors += n_items * max(
goal_min_width - 1, bin_width - goal_max_width)
max_errors += n_items * max(
goal_min_height - 1, bin_height - goal_max_height)
max_errors += max(goal_min_width, bin_width - goal_min_width)
max_errors += max(goal_max_width, bin_width - goal_max_width)
max_errors += max(goal_min_height, bin_height - goal_min_height)
max_errors += max(goal_max_height, bin_height - goal_max_height)

alt_area: Final[int] = (space.min_bins * bin_width * bin_height
- space.total_item_area)
if alt_area < 0:
raise ValueError("Invalid item area in space?")
max_errors += max(space.total_item_area, alt_area)

return max_errors

def __str__(self) -> str:
"""
Get the name of the errors objective function.
:return: `errors`
:retval "errors": always
"""
return "errors"
Loading

0 comments on commit 32454d4

Please sign in to comment.