From d7131e9f666a6e153c18e8a49cbe44334cf529f4 Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Fri, 21 Feb 2020 11:19:24 -0600 Subject: [PATCH] Knapsack: Make jitter relative instead of absolute --- docs/problems.md | 2 +- miplearn/problems/knapsack.py | 26 +++++++++++++-------- miplearn/problems/tests/test_knapsack.py | 29 ------------------------ 3 files changed, 17 insertions(+), 40 deletions(-) diff --git a/docs/problems.md b/docs/problems.md index d4cd016..579385a 100644 --- a/docs/problems.md +++ b/docs/problems.md @@ -91,7 +91,7 @@ from the provided probability distributions `K` and `u`. If `fix_w=True` is provided, then $w_{ij}$ are kept the same in all generated instances. This also implies that $n$ and $m$ are kept fixed. Although the prices and capacities are derived from $w_{ij}$, as long as `u` and `K` are not constants, the generated instances will still not be completely identical. -If a probability distribution `w_jitter` is provided, then item weights will be set to $w_{ij} + \gamma_{ij}$ where $\gamma_{ij}$ is sampled from `w_jitter`. When combined with `fix_w=True`, this argument may be used to generate instances where the weight of each item is roughly the same, but not exactly identical, across all instances. The prices of the items and the capacities of the knapsacks will be calculated as above, but using these perturbed weights instead. +If a probability distribution `w_jitter` is provided, then item weights will be set to $w_{ij} \gamma_{ij}$ where $\gamma_{ij}$ is sampled from `w_jitter`. When combined with `fix_w=True`, this argument may be used to generate instances where the weight of each item is roughly the same, but not exactly identical, across all instances. The prices of the items and the capacities of the knapsacks will be calculated as above, but using these perturbed weights instead. By default, all generated prices, weights and capacities are rounded to the nearest integer number. If `round=False` is provided, this rounding will be disabled. diff --git a/miplearn/problems/knapsack.py b/miplearn/problems/knapsack.py index a98fd0d..ce8a6ae 100644 --- a/miplearn/problems/knapsack.py +++ b/miplearn/problems/knapsack.py @@ -13,7 +13,7 @@ from scipy.stats.distributions import rv_frozen class ChallengeA: """ - 250 variables, 10 constraints, fixed weights - - w ~ U(100, 900), jitter ~ U(-100, 100) + - w ~ U(0, 1000), jitter ~ U(0.95, 1.05) - K = 500, u ~ U(0., 1.) - alpha = 0.25 """ @@ -25,12 +25,12 @@ class ChallengeA: np.random.seed(seed) self.gen = MultiKnapsackGenerator(n=randint(low=250, high=251), m=randint(low=10, high=11), - w=uniform(loc=100.0, scale=900.0), + w=uniform(loc=0.0, scale=1000.0), K=uniform(loc=500.0, scale=0.0), u=uniform(loc=0.0, scale=1.0), alpha=uniform(loc=0.25, scale=0.0), fix_w=True, - w_jitter=uniform(loc=-100.0, scale=200.0), + w_jitter=uniform(loc=0.95, scale=0.1), ) np.random.seed(seed + 1) self.training_instances = self.gen.generate(n_training_instances) @@ -91,8 +91,8 @@ class MultiKnapsackInstance(Instance): self.weights[:, index], ]) -# def get_variable_category(self, var, index): -# return index + def get_variable_category(self, var, index): + return index class MultiKnapsackGenerator: @@ -104,7 +104,7 @@ class MultiKnapsackGenerator: u=uniform(loc=0.0, scale=1.0), alpha=uniform(loc=0.25, scale=0.0), fix_w=False, - w_jitter=randint(low=0, high=1), + w_jitter=uniform(loc=1.0, scale=0.0), round=True, ): """Initialize the problem generator. @@ -133,7 +133,7 @@ class MultiKnapsackGenerator: be completely identical. If a probability distribution w_jitter is provided, then item weights will be set to - w[i,j] + gamma[i,j] where gamma[i,j] is sampled from w_jitter. When combined with + w[i,j] * gamma[i,j] where gamma[i,j] is sampled from w_jitter. When combined with fix_w=True, this argument may be used to generate instances where the weight of each item is roughly the same, but not exactly identical, across all instances. The prices of the items and the capacities of the knapsacks will be calculated as above, but using these @@ -186,10 +186,14 @@ class MultiKnapsackGenerator: self.fix_n = self.n.rvs() self.fix_m = self.m.rvs() self.fix_w = np.array([self.w.rvs(self.fix_n) for _ in range(self.fix_m)]) + self.fix_u = self.u.rvs(self.fix_n) + self.fix_K = self.K.rvs() else: self.fix_n = None self.fix_m = None self.fix_w = None + self.fix_u = None + self.fix_K = None def generate(self, n_samples): def _sample(): @@ -197,13 +201,15 @@ class MultiKnapsackGenerator: n = self.fix_n m = self.fix_m w = self.fix_w + u = self.fix_u + K = self.fix_K else: n = self.n.rvs() m = self.m.rvs() w = np.array([self.w.rvs(n) for _ in range(m)]) - w = w + np.array([self.w_jitter.rvs(n) for _ in range(m)]) - K = self.K.rvs() - u = self.u.rvs(n) + u = self.u.rvs(n) + K = self.K.rvs() + w = w * np.array([self.w_jitter.rvs(n) for _ in range(m)]) alpha = self.alpha.rvs(m) p = np.array([w[:,j].sum() / m + K * u[j] for j in range(n)]) b = np.array([w[i,:].sum() * alpha[i] for i in range(m)]) diff --git a/miplearn/problems/tests/test_knapsack.py b/miplearn/problems/tests/test_knapsack.py index 2d6b1b6..fb179e5 100644 --- a/miplearn/problems/tests/test_knapsack.py +++ b/miplearn/problems/tests/test_knapsack.py @@ -23,32 +23,3 @@ def test_knapsack_generator(): assert round(np.mean(w_sum), -1) == 500. assert round(np.mean(p_sum), -1) == 1250. assert round(np.mean(b_sum), -3) == 25000. - - -def test_knapsack_fixed_weights_jitter(): - gen = MultiKnapsackGenerator(n=randint(low=50, high=51), - m=randint(low=10, high=11), - w=randint(low=0, high=1000), - K=randint(low=500, high=501), - u=uniform(loc=1.0, scale=0.0), - alpha=uniform(loc=0.50, scale=0.0), - fix_w=True, - w_jitter=randint(low=0, high=1), - ) - instances = gen.generate(100) - w = [instance.weights[0,0] for instance in instances] - assert np.std(w) == 0. - - gen = MultiKnapsackGenerator(n=randint(low=1, high=2), - m=randint(low=10, high=11), - w=randint(low=1000, high=1001), - K=randint(low=500, high=501), - u=uniform(loc=1.0, scale=0.0), - alpha=uniform(loc=0.50, scale=0.0), - fix_w=True, - w_jitter=randint(low=0, high=1001), - ) - instances = gen.generate(5_000) - w = [instance.weights[0,0] for instance in instances] - assert round(np.std(w), -1) == 290. - assert round(np.mean(w), -2) == 1500. \ No newline at end of file