Implement BinPackPerturber

This commit is contained in:
2025-12-08 13:16:23 -06:00
parent a4cb46f73e
commit 9192bb02eb
3 changed files with 111 additions and 100 deletions

View File

@@ -34,19 +34,10 @@ class BinPackData:
class BinPackGenerator:
"""Random instance generator for the bin packing problem.
If `fix_items=False`, the class samples the user-provided probability distributions
Generates instances by sampling the user-provided probability distributions
n, sizes and capacity to decide, respectively, the number of items, the sizes of
the items and capacity of the bin. All values are sampled independently.
If `fix_items=True`, the class creates a reference instance, using the method
previously described, then generates additional instances by perturbing its item
sizes and bin capacity. More specifically, the sizes of the items are set to `s_i
* gamma_i` where `s_i` is the size of the i-th item in the reference instance and
`gamma_i` is sampled from `sizes_jitter`. Similarly, the bin capacity is set to `B *
beta`, where `B` is the reference bin capacity and `beta` is sampled from
`capacity_jitter`. The number of items remains the same across all generated
instances.
Args
----
n
@@ -55,13 +46,6 @@ class BinPackGenerator:
Probability distribution for the item sizes.
capacity
Probability distribution for the bin capacity.
sizes_jitter
Probability distribution for the item size randomization.
capacity_jitter
Probability distribution for the bin capacity.
fix_items
If `True`, generates a reference instance, then applies some perturbation to it.
If `False`, generates completely different instances.
"""
def __init__(
@@ -69,17 +53,10 @@ class BinPackGenerator:
n: rv_frozen,
sizes: rv_frozen,
capacity: rv_frozen,
sizes_jitter: rv_frozen,
capacity_jitter: rv_frozen,
fix_items: bool,
) -> None:
self.n = n
self.sizes = sizes
self.capacity = capacity
self.sizes_jitter = sizes_jitter
self.capacity_jitter = capacity_jitter
self.fix_items = fix_items
self.ref_data: Optional[BinPackData] = None
def generate(self, n_samples: int) -> List[BinPackData]:
"""Generates random instances.
@@ -91,22 +68,62 @@ class BinPackGenerator:
"""
def _sample() -> BinPackData:
if self.ref_data is None:
n = self.n.rvs()
sizes = self.sizes.rvs(n)
capacity = self.capacity.rvs()
if self.fix_items:
self.ref_data = BinPackData(sizes, capacity)
else:
n = self.ref_data.sizes.shape[0]
sizes = self.ref_data.sizes
capacity = self.ref_data.capacity
sizes = sizes * self.sizes_jitter.rvs(n)
capacity = capacity * self.capacity_jitter.rvs()
n = self.n.rvs()
sizes = self.sizes.rvs(n)
capacity = self.capacity.rvs()
return BinPackData(sizes.round(2), capacity.round(2))
return [_sample() for n in range(n_samples)]
return [_sample() for _ in range(n_samples)]
class BinPackPerturber:
"""Perturbation generator for existing bin packing instances.
Takes an existing BinPackData instance and generates new instances by perturbing
its item sizes and bin capacity. The sizes of the items are set to `s_i * gamma_i`
where `s_i` is the size of the i-th item in the reference instance and `gamma_i`
is sampled from `sizes_jitter`. Similarly, the bin capacity is set to `B * beta`,
where `B` is the reference bin capacity and `beta` is sampled from `capacity_jitter`.
The number of items remains the same across all generated instances.
Args
----
sizes_jitter
Probability distribution for the item size randomization.
capacity_jitter
Probability distribution for the bin capacity randomization.
"""
def __init__(
self,
sizes_jitter: rv_frozen,
capacity_jitter: rv_frozen,
) -> None:
self.sizes_jitter = sizes_jitter
self.capacity_jitter = capacity_jitter
def perturb(
self,
instance: BinPackData,
n_samples: int,
) -> List[BinPackData]:
"""Generates perturbed instances.
Parameters
----------
instance
The reference instance to perturb.
n_samples
Number of samples to generate.
"""
def _sample() -> BinPackData:
n = instance.sizes.shape[0]
sizes = instance.sizes * self.sizes_jitter.rvs(n)
capacity = instance.capacity * self.capacity_jitter.rvs()
return BinPackData(sizes.round(2), capacity.round(2))
return [_sample() for _ in range(n_samples)]
def build_binpack_model_gurobipy(data: Union[str, BinPackData]) -> GurobiModel: