pykp 3.1.2__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pykp-3.1.2 → pykp-3.2.0}/PKG-INFO +1 -1
- {pykp-3.1.2 → pykp-3.2.0}/pykp/metrics.py +31 -18
- {pykp-3.1.2 → pykp-3.2.0}/pykp.egg-info/PKG-INFO +1 -1
- {pykp-3.1.2 → pykp-3.2.0}/pyproject.toml +1 -1
- {pykp-3.1.2 → pykp-3.2.0}/tests/test_metrics.py +33 -2
- {pykp-3.1.2 → pykp-3.2.0}/LICENSE +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/README.md +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp/__init__.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp/arrangement.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp/item.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp/knapsack.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp/sampler.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp/solvers.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp.egg-info/SOURCES.txt +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp.egg-info/dependency_links.txt +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp.egg-info/requires.txt +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/pykp.egg-info/top_level.txt +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/setup.cfg +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/setup.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/tests/test_knapsack.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/tests/test_sampler.py +0 -0
- {pykp-3.1.2 → pykp-3.2.0}/tests/test_solvers.py +0 -0
|
@@ -41,19 +41,19 @@ def _initialise_grid(
|
|
|
41
41
|
The first array corresponds to normalised capacities, and the second
|
|
42
42
|
to normalised profits.
|
|
43
43
|
"""
|
|
44
|
-
norm_c_step_size = 1 / resolution[
|
|
45
|
-
norm_p_step_size = 1 / resolution[
|
|
44
|
+
norm_c_step_size = 1 / resolution[1]
|
|
45
|
+
norm_p_step_size = 1 / resolution[0]
|
|
46
46
|
|
|
47
47
|
grid = np.meshgrid(
|
|
48
|
-
np.linspace(0, 1 - norm_c_step_size, resolution[
|
|
49
|
-
np.linspace(1 - norm_p_step_size, 0, resolution[
|
|
48
|
+
np.linspace(0, 1 - norm_c_step_size, resolution[1]),
|
|
49
|
+
np.linspace(1 - norm_p_step_size, 0, resolution[0]),
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
return grid
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def _sample_instance(
|
|
56
|
-
num_items: int, norm_c: float, norm_p: float
|
|
56
|
+
num_items: int, norm_c: float, norm_p: float, rng: np.random.Generator
|
|
57
57
|
) -> tuple[list[Item], float, float]:
|
|
58
58
|
"""
|
|
59
59
|
Sample a knapsack instance based on normalised capacity and profit targets.
|
|
@@ -71,6 +71,8 @@ def _sample_instance(
|
|
|
71
71
|
Normalised capacity factor to scale the total weight.
|
|
72
72
|
norm_p : float
|
|
73
73
|
Normalised profit factor to scale the total value.
|
|
74
|
+
rng : np.random.Generator
|
|
75
|
+
Random number generator for sampling.
|
|
74
76
|
|
|
75
77
|
Returns
|
|
76
78
|
-------
|
|
@@ -81,8 +83,8 @@ def _sample_instance(
|
|
|
81
83
|
target_profit : float
|
|
82
84
|
The scaled target profit.
|
|
83
85
|
"""
|
|
84
|
-
weights =
|
|
85
|
-
profits =
|
|
86
|
+
weights = rng.uniform(0.001, 1, num_items)
|
|
87
|
+
profits = rng.uniform(0.001, 1, num_items)
|
|
86
88
|
|
|
87
89
|
capacity = sum(weights) * norm_c
|
|
88
90
|
target_profit = sum(profits) * norm_p
|
|
@@ -99,6 +101,7 @@ def _simulate_cell_solvability(
|
|
|
99
101
|
num_items: int,
|
|
100
102
|
samples: int,
|
|
101
103
|
solver: Callable,
|
|
104
|
+
rng: np.random.Generator,
|
|
102
105
|
progress: tqdm,
|
|
103
106
|
) -> float:
|
|
104
107
|
"""Estimate the solvability of a grid cell.
|
|
@@ -121,6 +124,8 @@ def _simulate_cell_solvability(
|
|
|
121
124
|
solver : Callable
|
|
122
125
|
A solver function that takes items and capacity as arguments and
|
|
123
126
|
returns an optimal node or array of optimal nodes.
|
|
127
|
+
rng : np.random.Generator
|
|
128
|
+
Random number generator for sampling.
|
|
124
129
|
progress : tqdm
|
|
125
130
|
Progress bar for tracking iterations.
|
|
126
131
|
|
|
@@ -133,11 +138,14 @@ def _simulate_cell_solvability(
|
|
|
133
138
|
"""
|
|
134
139
|
score = 0
|
|
135
140
|
for _ in range(samples):
|
|
136
|
-
norm_c_draw =
|
|
137
|
-
norm_p_draw =
|
|
141
|
+
norm_c_draw = rng.uniform(norm_c_range[0], norm_c_range[1])
|
|
142
|
+
norm_p_draw = rng.uniform(norm_p_range[0], norm_p_range[1])
|
|
138
143
|
|
|
139
144
|
items, capacity, target_profit = _sample_instance(
|
|
140
|
-
num_items=num_items,
|
|
145
|
+
num_items=num_items,
|
|
146
|
+
norm_c=norm_c_draw,
|
|
147
|
+
norm_p=norm_p_draw,
|
|
148
|
+
rng=rng,
|
|
141
149
|
)
|
|
142
150
|
result = solver(items=items, capacity=capacity)
|
|
143
151
|
if isinstance(result, list):
|
|
@@ -187,6 +195,7 @@ def phase_transition(
|
|
|
187
195
|
samples: int = 100,
|
|
188
196
|
solver: str = "branch_and_bound",
|
|
189
197
|
resolution: tuple[int, int] = (41, 41),
|
|
198
|
+
seed: int | None = None,
|
|
190
199
|
path: str = None,
|
|
191
200
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
192
201
|
"""Compute the phase transition matrix for knapsack instances.
|
|
@@ -218,9 +227,10 @@ def phase_transition(
|
|
|
218
227
|
|
|
219
228
|
Returns
|
|
220
229
|
-------
|
|
221
|
-
|
|
222
|
-
The
|
|
223
|
-
|
|
230
|
+
coordinate_matrices : tuple of np.ndarray
|
|
231
|
+
The coordinate matrices for normalised capacities and normalised
|
|
232
|
+
profits. The first matrix corresponds to normalised capacities,
|
|
233
|
+
and the second to normalised profits.
|
|
224
234
|
phase_transition : np.ndarray
|
|
225
235
|
A 2D matrix of solvability (ranging from 0.0 to 1.0) corresponding
|
|
226
236
|
to each cell in `grid`.
|
|
@@ -233,9 +243,12 @@ def phase_transition(
|
|
|
233
243
|
resolution: tuple[int, int], optional
|
|
234
244
|
Resolution of the normalised capacity-normalised profit grid.
|
|
235
245
|
The first element corresponds to the resolution of normalised
|
|
236
|
-
|
|
246
|
+
profit, and the second to the resolution of normalised capacity.
|
|
237
247
|
Defaults to (41, 41).
|
|
238
|
-
|
|
248
|
+
seed : int | None, optional
|
|
249
|
+
Seed for sampling. Defaults to None.
|
|
250
|
+
path str, optional:
|
|
251
|
+
Path to save the phase transition to. Defaults
|
|
239
252
|
to None.
|
|
240
253
|
|
|
241
254
|
Examples
|
|
@@ -309,6 +322,7 @@ def phase_transition(
|
|
|
309
322
|
[(p, p + 1 / resolution[1]) for p in grid[1].flatten()],
|
|
310
323
|
)
|
|
311
324
|
)
|
|
325
|
+
rng = np.random.default_rng(seed)
|
|
312
326
|
|
|
313
327
|
phase_transition = []
|
|
314
328
|
with tqdm(total=samples * len(points)) as progress:
|
|
@@ -320,12 +334,11 @@ def phase_transition(
|
|
|
320
334
|
samples=samples,
|
|
321
335
|
solver=solver,
|
|
322
336
|
progress=progress,
|
|
337
|
+
rng=rng,
|
|
323
338
|
)
|
|
324
339
|
phase_transition.append(solvability)
|
|
325
340
|
|
|
326
|
-
phase_transition = np.array(phase_transition).reshape(
|
|
327
|
-
(resolution[0], resolution[1])
|
|
328
|
-
)
|
|
341
|
+
phase_transition = np.array(phase_transition).reshape(resolution)
|
|
329
342
|
|
|
330
343
|
if path:
|
|
331
344
|
_save_phase_transition(phase_transition, grid, path)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pykp"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.2.0"
|
|
8
8
|
description = "Tooling for sampling and solving instances of the 0-1 Knapsack Problem."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Hassan Andrabi", email = "hrs.andrabi@gmail.com" }
|
|
@@ -22,8 +22,8 @@ def test_phase_transition_returns_correct_shape(resolution):
|
|
|
22
22
|
resolution=resolution,
|
|
23
23
|
path=None,
|
|
24
24
|
)
|
|
25
|
-
assert grid[0].shape ==
|
|
26
|
-
assert grid[1].shape ==
|
|
25
|
+
assert grid[0].shape == resolution
|
|
26
|
+
assert grid[1].shape == resolution
|
|
27
27
|
|
|
28
28
|
assert solvability_matrix.shape == resolution
|
|
29
29
|
|
|
@@ -97,3 +97,34 @@ def test_phase_transition_values_in_range():
|
|
|
97
97
|
assert np.all(solvability_matrix >= 0.0) and np.all(
|
|
98
98
|
solvability_matrix <= 1.0
|
|
99
99
|
), "Solvability values are not all within [0, 1]."
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pytest.mark.parametrize("seed", [1, 2, 3])
|
|
103
|
+
def test_phase_transition_reproducibility(seed):
|
|
104
|
+
"""Test that the phase transition is reproducible."""
|
|
105
|
+
resolution = (2, 2)
|
|
106
|
+
num_items = 5
|
|
107
|
+
samples = 5
|
|
108
|
+
grid, solvability_matrix = metrics.phase_transition(
|
|
109
|
+
num_items=num_items,
|
|
110
|
+
samples=samples,
|
|
111
|
+
solver="branch_and_bound",
|
|
112
|
+
resolution=resolution,
|
|
113
|
+
path=None,
|
|
114
|
+
seed=seed,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
grid2, solvability_matrix2 = metrics.phase_transition(
|
|
118
|
+
num_items=num_items,
|
|
119
|
+
samples=samples,
|
|
120
|
+
solver="branch_and_bound",
|
|
121
|
+
resolution=resolution,
|
|
122
|
+
path=None,
|
|
123
|
+
seed=seed,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
assert np.all(grid[0] == grid2[0]), "Inconsistent grid."
|
|
127
|
+
assert np.all(grid[0] == grid2[0]), "Inconsistent grid."
|
|
128
|
+
assert np.all(solvability_matrix == solvability_matrix2), (
|
|
129
|
+
"Inconsistent solvability matrix."
|
|
130
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|