mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-08 18:38:51 -06:00
Implement BinPackPerturber
This commit is contained in:
@@ -87,11 +87,12 @@
|
||||
"\n",
|
||||
"Random instances of the bin packing problem can be generated using the class [BinPackGenerator][BinPackGenerator].\n",
|
||||
"\n",
|
||||
"If `fix_items=False`, the class samples 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.\n",
|
||||
"The class samples 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.\n",
|
||||
"\n",
|
||||
"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 size is set to $B \\beta$, where $B$ is the reference bin size and $\\beta$ is sampled from `capacity_jitter`. The number of items remains the same across all generated instances.\n",
|
||||
"To create multiple instances with the same items but different sizes and capacities, you can use [BinPackPerturber][BinPackPerturber]. This class takes an existing BinPackData instance and generates new 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 size is set to $B \\beta$, where $B$ is the reference bin size and $\\beta$ is sampled from `capacity_jitter`. The number of items remains the same across all generated instances.\n",
|
||||
"\n",
|
||||
"[BinPackGenerator]: ../../api/problems/#miplearn.problems.binpack.BinPackGenerator"
|
||||
"[BinPackGenerator]: ../../api/problems/#miplearn.problems.binpack.BinPackGenerator\n",
|
||||
"[BinPackPerturber]: ../../api/problems/#miplearn.problems.binpack.BinPackPerturber"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -104,7 +105,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 2,
|
||||
"id": "f14e560c-ef9f-4c48-8467-72d6acce5f9f",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
@@ -118,25 +119,24 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"0 [ 8.47 26. 19.52 14.11 3.65 3.65 1.4 21.76 14.82 16.96] 102.24\n",
|
||||
"1 [ 8.69 22.78 17.81 14.83 4.12 3.67 1.46 22.05 13.66 18.08] 93.41\n",
|
||||
"2 [ 8.55 25.9 20. 15.89 3.75 3.59 1.51 21.4 13.89 17.68] 90.69\n",
|
||||
"3 [10.13 22.62 18.89 14.4 3.92 3.94 1.36 23.69 15.85 19.26] 107.9\n",
|
||||
"4 [ 9.55 25.77 16.79 14.06 3.55 3.76 1.42 20.66 16.02 17.19] 95.62\n",
|
||||
"5 [ 9.44 22.06 19.41 13.69 4.28 4.11 1.36 19.51 15.98 18.43] 104.58\n",
|
||||
"6 [ 9.87 21.74 17.78 13.82 4.18 4. 1.4 19.76 14.46 17.08] 104.59\n",
|
||||
"7 [ 9.62 25.61 18.2 13.83 4.07 4.1 1.47 22.83 15.01 17.78] 98.55\n",
|
||||
"8 [ 8.47 21.9 16.58 15.37 3.76 3.91 1.57 20.57 14.76 18.61] 94.58\n",
|
||||
"9 [ 8.57 22.77 17.06 16.25 4.14 4. 1.56 22.97 14.09 19.09] 100.79\n",
|
||||
"0 [ 8.46 26. 19.52 14.11 3.65 3.65 1.39 21.76 14.83 16.96] 102.24\n",
|
||||
"1 [ 8.69 22.78 17.81 14.84 4.12 3.67 1.45 22.05 13.67 18.08] 93.41\n",
|
||||
"2 [ 8.55 25.9 20. 15.89 3.75 3.59 1.5 21.39 13.89 17.68] 90.69\n",
|
||||
"3 [10.13 22.62 18.89 14.41 3.92 3.94 1.36 23.68 15.86 19.26] 107.9\n",
|
||||
"4 [ 9.54 25.78 16.79 14.06 3.55 3.76 1.42 20.66 16.02 17.19] 95.62\n",
|
||||
"5 [ 9.44 22.06 19.41 13.7 4.28 4.11 1.36 19.51 15.98 18.43] 104.58\n",
|
||||
"6 [ 9.87 21.75 17.78 13.82 4.18 4. 1.4 19.76 14.46 17.08] 104.59\n",
|
||||
"7 [ 9.62 25.61 18.2 13.83 4.07 4.1 1.47 22.82 15.01 17.78] 98.55\n",
|
||||
"8 [ 8.47 21.91 16.59 15.38 3.76 3.91 1.57 20.56 14.76 18.6 ] 94.58\n",
|
||||
"9 [ 8.57 22.77 17.06 16.26 4.14 4. 1.56 22.96 14.09 19.09] 100.79\n",
|
||||
"\n",
|
||||
"Set parameter LicenseID to value 389975\n",
|
||||
"Gurobi Optimizer version 13.0.0 build v13.0.0rc1 (linux64 - \"Ubuntu 22.04.5 LTS\")\n",
|
||||
"\n",
|
||||
"CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]\n",
|
||||
"Thread count: 16 physical cores, 16 logical processors, using up to 16 threads\n",
|
||||
"\n",
|
||||
"Optimize a model with 20 rows, 110 columns and 210 nonzeros (Min)\n",
|
||||
"Model fingerprint: 0x1ff9913f\n",
|
||||
"Model fingerprint: 0xde862da6\n",
|
||||
"Model has 10 linear objective coefficients\n",
|
||||
"Variable types: 0 continuous, 110 integer (110 binary)\n",
|
||||
"Coefficient statistics:\n",
|
||||
@@ -149,18 +149,18 @@
|
||||
"Presolved: 20 rows, 110 columns, 210 nonzeros\n",
|
||||
"Variable types: 0 continuous, 110 integer (110 binary)\n",
|
||||
"\n",
|
||||
"Root relaxation: objective 1.274844e+00, 38 iterations, 0.00 seconds (0.00 work units)\n",
|
||||
"Root relaxation: objective 1.274746e+00, 39 iterations, 0.00 seconds (0.00 work units)\n",
|
||||
"\n",
|
||||
" Nodes | Current Node | Objective Bounds | Work\n",
|
||||
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
|
||||
"\n",
|
||||
" 0 0 1.27484 0 4 5.00000 1.27484 74.5% - 0s\n",
|
||||
"H 0 0 4.0000000 1.27484 68.1% - 0s\n",
|
||||
"H 0 0 3.0000000 1.27484 57.5% - 0s\n",
|
||||
"H 0 0 2.0000000 1.27484 36.3% - 0s\n",
|
||||
" 0 0 1.27484 0 4 2.00000 1.27484 36.3% - 0s\n",
|
||||
" 0 0 1.27475 0 4 5.00000 1.27475 74.5% - 0s\n",
|
||||
"H 0 0 4.0000000 1.27475 68.1% - 0s\n",
|
||||
"H 0 0 3.0000000 1.27475 57.5% - 0s\n",
|
||||
"H 0 0 2.0000000 1.27475 36.3% - 0s\n",
|
||||
" 0 0 1.27475 0 4 2.00000 1.27475 36.3% - 0s\n",
|
||||
"\n",
|
||||
"Explored 1 nodes (38 simplex iterations) in 0.00 seconds (0.00 work units)\n",
|
||||
"Explored 1 nodes (39 simplex iterations) in 0.01 seconds (0.00 work units)\n",
|
||||
"Thread count was 16 (of 16 available processors)\n",
|
||||
"\n",
|
||||
"Solution count 4: 2 3 4 5 \n",
|
||||
@@ -170,33 +170,30 @@
|
||||
"\n",
|
||||
"User-callback calls 149, time in user-callback 0.00 sec\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/axavier/Notebooks/[2025-12] Parallel BB Datasets/venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
|
||||
" from .autonotebook import tqdm as notebook_tqdm\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"from scipy.stats import uniform, randint\n",
|
||||
"from miplearn.problems.binpack import BinPackGenerator, build_binpack_model_gurobipy\n",
|
||||
"from miplearn.problems.binpack import BinPackGenerator, BinPackPerturber, build_binpack_model_gurobipy\n",
|
||||
"\n",
|
||||
"# Set random seed, to make example reproducible\n",
|
||||
"np.random.seed(42)\n",
|
||||
"\n",
|
||||
"# Generate random instances of the binpack problem with ten items\n",
|
||||
"data = BinPackGenerator(\n",
|
||||
"# Generate a reference instance with ten items\n",
|
||||
"generator = BinPackGenerator(\n",
|
||||
" n=randint(low=10, high=11),\n",
|
||||
" sizes=uniform(loc=0, scale=25),\n",
|
||||
" capacity=uniform(loc=100, scale=0),\n",
|
||||
")\n",
|
||||
"reference_instance = generator.generate(1)[0]\n",
|
||||
"\n",
|
||||
"# Generate perturbed instances using the reference\n",
|
||||
"perturber = BinPackPerturber(\n",
|
||||
" sizes_jitter=uniform(loc=0.9, scale=0.2),\n",
|
||||
" capacity_jitter=uniform(loc=0.9, scale=0.2),\n",
|
||||
" fix_items=True,\n",
|
||||
").generate(10)\n",
|
||||
")\n",
|
||||
"data = perturber.perturb(reference_instance, 10)\n",
|
||||
"\n",
|
||||
"# Print sizes and capacities\n",
|
||||
"for i in range(10):\n",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -18,37 +18,34 @@ def test_binpack_generator() -> None:
|
||||
n=randint(low=10, high=11),
|
||||
sizes=uniform(loc=0, scale=10),
|
||||
capacity=uniform(loc=100, scale=0),
|
||||
sizes_jitter=uniform(loc=0.9, scale=0.2),
|
||||
capacity_jitter=uniform(loc=0.9, scale=0.2),
|
||||
fix_items=True,
|
||||
)
|
||||
data = gen.generate(2)
|
||||
assert data[0].sizes.tolist() == [
|
||||
3.39,
|
||||
10.4,
|
||||
7.81,
|
||||
5.64,
|
||||
1.46,
|
||||
1.46,
|
||||
0.56,
|
||||
8.7,
|
||||
5.93,
|
||||
6.79,
|
||||
]
|
||||
assert data[0].capacity == 102.24
|
||||
assert data[1].sizes.tolist() == [
|
||||
3.48,
|
||||
9.11,
|
||||
7.12,
|
||||
5.93,
|
||||
1.65,
|
||||
1.47,
|
||||
3.75,
|
||||
9.51,
|
||||
7.32,
|
||||
5.99,
|
||||
1.56,
|
||||
1.56,
|
||||
0.58,
|
||||
8.82,
|
||||
5.47,
|
||||
7.23,
|
||||
8.66,
|
||||
6.01,
|
||||
7.08,
|
||||
]
|
||||
assert data[1].capacity == 93.41
|
||||
assert data[0].capacity == 100.0
|
||||
assert data[1].sizes.tolist() == [
|
||||
0.21,
|
||||
9.7,
|
||||
8.32,
|
||||
2.12,
|
||||
1.82,
|
||||
1.83,
|
||||
3.04,
|
||||
5.25,
|
||||
4.32,
|
||||
2.91,
|
||||
]
|
||||
assert data[1].capacity == 100.0
|
||||
|
||||
|
||||
def test_binpack() -> None:
|
||||
|
||||
Reference in New Issue
Block a user