Skip to content

Commit 8f55468

Browse files
committed
ilp_avg
1 parent 6f7d09e commit 8f55468

File tree

5 files changed

+37
-33
lines changed

5 files changed

+37
-33
lines changed

prtpy/VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.8.3
1+
0.8.4

prtpy/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class partitioning:
2020

2121
from prtpy.partitioning.integer_programming import optimal as ilp
2222
from prtpy.partitioning.integer_programming import optimal as integer_programming
23+
from prtpy.partitioning.integer_programming_avg import optimal as ilp_avg
2324

2425
from prtpy.partitioning.greedy import greedy
2526
from prtpy.partitioning.greedy import greedy as lpt

prtpy/partitioning/complete_greedy.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323

2424

2525
def anytime(
26-
binner: Binner, numbins: int, items: List[int], relative_value: List[int] = None,
26+
binner: Binner, numbins: int, items: List[int],
27+
entitlements: List[int] = None,
2728
objective: obj.Objective = obj.MinimizeDifference,
2829
use_lower_bound: bool = True,
2930
# Prune branches whose lower bound (= optimistic value) is at least as large as the current minimum.
@@ -64,10 +65,14 @@ def anytime(
6465
Bin #0: [46, 10], sum=56.0
6566
Bin #1: [27, 16, 13], sum=56.0
6667
Bin #2: [39, 26], sum=65.0
68+
69+
# Minimize the distance to the average - equal rights:
6770
>>> printbins(anytime(BinnerKeepingContents(), 3, walter_numbers, objective=obj.MinimizeDistAvg))
6871
Bin #0: [39, 16], sum=55.0
6972
Bin #1: [46, 13], sum=59.0
7073
Bin #2: [27, 26, 10], sum=63.0
74+
75+
# Minimize the distance to the average - different rights:
7176
>>> printbins(anytime(BinnerKeepingContents(), 3, walter_numbers,[0.2,0.4,0.4], objective=obj.MinimizeDistAvg))
7277
Bin #0: [27, 10], sum=37.0
7378
Bin #1: [39, 16, 13], sum=68.0
@@ -171,9 +176,9 @@ def anytime(
171176
# we add a sum to each bin in order to equal them out to the bin with the highest relative value
172177
# (at the end of the algorithm we will remove these sums).
173178
first_bins = binner.new_bins(numbins)
174-
if (relative_value):
179+
if (entitlements):
175180
for i in range(numbins):
176-
binner.add_item_to_bin(first_bins, (max(relative_value) * sum(items) - relative_value[i] * sum(items)), i)
181+
binner.add_item_to_bin(first_bins, (max(entitlements) * sum(items) - entitlements[i] * sum(items)), i)
177182
first_vertex = (first_bins, 0)
178183
stack: List[Tuple[BinsArray, int]] = [first_vertex]
179184
if use_set_of_seen_states:
@@ -250,13 +255,13 @@ def anytime(
250255
new_smallest_sum = current_sums[0]
251256
fast_lower_bound = -(new_smallest_sum + sum_of_remaining_items)
252257
elif objective == obj.MinimizeDistAvg:
253-
if relative_value:
258+
if entitlements:
254259
fast_lower_bound = 0
255260
for i in range (numbins):
256-
# For each bin: we take off the sum that we added in the beginning of the algorithm (max(relative_value) * sum(items) - relative_value[i] * sum(items))
257-
# Then we check if the difference between the bin's sum and the relative AVG for bin i: (sum(items)*relative_value[i])
261+
# For each bin: we take off the sum that we added in the beginning of the algorithm (max(entitlements) * sum(items) - entitlements[i] * sum(items))
262+
# Then we check if the difference between the bin's sum and the relative AVG for bin i: (sum(items)*entitlements[i])
258263
# is positive and contributes to our final difference or negative and we will not add anything to our difference.
259-
fast_lower_bound = fast_lower_bound + max((current_sums[i]-(max(relative_value) * sum(items) - relative_value[i] * sum(items)))-sum(items)*relative_value[i],0)
264+
fast_lower_bound = fast_lower_bound + max((current_sums[i]-(max(entitlements) * sum(items) - entitlements[i] * sum(items)))-sum(items)*entitlements[i],0)
260265
else:
261266
fast_lower_bound = 0
262267
avg = sum(items) / numbins
@@ -269,7 +274,7 @@ def anytime(
269274
continue
270275

271276
new_bins = binner.add_item_to_bin(binner.copy_bins(current_bins), next_item, bin_index)
272-
if not relative_value:
277+
if not entitlements:
273278
binner.sort_by_ascending_sum(new_bins)
274279
new_sums = tuple(binner.sums(new_bins))
275280

@@ -298,7 +303,7 @@ def anytime(
298303
times_heuristic_3_activated)
299304

300305

301-
if (relative_value):
306+
if (entitlements):
302307
# For each bin we remove the value that we added in the beginning of the algorithm.
303308
for i in range(numbins):
304309
binner.remove_item_from_bin(best_bins, i, 0)

prtpy/partitioning/integer_programming.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def optimal(
2424
copies=1,
2525
time_limit=inf,
2626
additional_constraints:Callable=lambda sums:[],
27-
weights:List[float]=None,
27+
entitlements:List[float]=None,
2828
verbose=0,
2929
solver_name = mip.CBC, # passed to MIP. See https://docs.python-mip.com/en/latest/quickstart.html#creating-models.
3030
# solver_name = mip.GRB, # passed to MIP. See https://docs.python-mip.com/en/latest/quickstart.html#creating-models.
@@ -42,7 +42,7 @@ def optimal(
4242
:param copies: how many copies there are of each item. Default: 1.
4343
:param time_limit: stop the computation after this number of seconds have passed.
4444
:param additional_constraints: a function that accepts the list of sums in ascending order, and returns a list of possible additional constraints on the sums.
45-
:param weights: if given, must be of size bins.num. Divides each sum by its weight before applying the objective function.
45+
:param entitlements: if given, must be of size bins.num. Divides each sum by its weight before applying the objective function.
4646
:param solver_name: passed to MIP. See https://docs.python-mip.com/en/latest/quickstart.html#creating-models
4747
:param model_filename: if not None, the MIP model will be written into this file, for debugging. NOTE: The extension should be either ".lp" or ".mps" (it indicates the output format)
4848
:param solution_filename: if not None, the solution will be written into this file, for debugging.
@@ -76,11 +76,11 @@ def optimal(
7676
array([56., 56., 65.])
7777
7878
>>> items = [11.1, 11, 11, 11, 22]
79-
>>> optimal(BinnerKeepingSums(), 2, items, objective=obj.MaximizeSmallestSum, weights=[1,1])
79+
>>> optimal(BinnerKeepingSums(), 2, items, objective=obj.MaximizeSmallestSum, entitlements=[1,1])
8080
array([33. , 33.1])
81-
>>> optimal(BinnerKeepingSums(), 2, items, objective=obj.MaximizeSmallestSum, weights=[1,2])
81+
>>> optimal(BinnerKeepingSums(), 2, items, objective=obj.MaximizeSmallestSum, entitlements=[1,2])
8282
array([22. , 44.1])
83-
>>> optimal(BinnerKeepingSums(), 2, items, objective=obj.MaximizeSmallestSum, weights=[10,2])
83+
>>> optimal(BinnerKeepingSums(), 2, items, objective=obj.MaximizeSmallestSum, entitlements=[10,2])
8484
array([11.1, 55. ])
8585
8686
>>> from prtpy import partition
@@ -103,21 +103,25 @@ def optimal(
103103
Bin #3: [62, 187], sum=249.0
104104
Bin #4: [93, 158], sum=251.0
105105
"""
106+
if objective == obj.MinimizeDistAvg:
107+
from prtpy.partitioning.integer_programming_avg import optimal as optimal_avg
108+
return optimal_avg(binner, numbins, items, entitlements=entitlements, copies=copies, time_limit=time_limit, verbose=verbose, solver_name=solver_name, model_filename=model_filename, solution_filename=solution_filename)
109+
106110
ibins = range(numbins)
107111
items = list(items)
108112
iitems = range(len(items))
109113
if isinstance(copies, Number):
110114
copies = {iitem: copies for iitem in iitems}
111-
if weights is None:
112-
weights = numbins*[1]
115+
if entitlements is None:
116+
entitlements = numbins*[1]
113117

114118
model = mip.Model(name = '', solver_name=solver_name)
115119
counts: dict = {
116120
iitem: [model.add_var(var_type=mip.INTEGER, name=f'item{iitem}_in_bin{ibin}') for ibin in ibins]
117121
for iitem in iitems
118122
} # counts[i][j] is a variable that represents how many times item i appears in bin j.
119123
bin_sums = [
120-
sum([counts[iitem][ibin] * binner.valueof(items[iitem]) for iitem in iitems])/weights[ibin]
124+
sum([counts[iitem][ibin] * binner.valueof(items[iitem]) for iitem in iitems])/entitlements[ibin]
121125
for ibin in ibins
122126
] # bin_sums[j] is a variable-expression that represents the sum of values in bin j.
123127

prtpy/partitioning/integer_programming_avg.py

+9-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import mip
1616

1717
def optimal(
18-
binner: Binner, numbins: int, items: List[any], relative_values: List[any] = None,
18+
binner: Binner, numbins: int, items: List[any], entitlements: List[any] = None,
1919
copies=1,
2020
time_limit=inf,
2121
verbose=0,
@@ -29,7 +29,7 @@ def optimal(
2929
3030
:param numbins: number of bins.
3131
:param items: list of items.
32-
:param relative_values: list of relative values that sum up to 1 for the bins if there are any.
32+
:param entitlements: list of relative values that sum up to 1 for the bins if there are any.
3333
:param copies: how many copies there are of each item. Default: 1.
3434
:param time_limit: stop the computation after this number of seconds have passed.
3535
:param valueof: a function that maps an item from the list `items` to a number representing its value.
@@ -101,16 +101,11 @@ def optimal(
101101
for i in range (len(items)):
102102
sum_items = sum_items + items[i] * copies[i]
103103

104-
if relative_values:
105-
z_js = [
106-
bin_sums[ibin] - sum_items * relative_values[ibin]
107-
for ibin in ibins
108-
]
109-
else:
110-
z_js = [
111-
bin_sums[ibin] - sum_items / len(ibins)
112-
for ibin in ibins
113-
]
104+
effective_entitlements = entitlements or [1. / numbins for ibin in ibins]
105+
z_js = [
106+
bin_sums[ibin] - sum_items * effective_entitlements[ibin]
107+
for ibin in ibins
108+
]
114109

115110
t_js = [
116111
model.add_var(var_type=mip.INTEGER) for ibin in ibins
@@ -147,7 +142,7 @@ def optimal(
147142
count_item_in_bin = int(counts[iitem][ibin].x)
148143
for _ in range(count_item_in_bin):
149144
binner.add_item_to_bin(output, items[iitem], ibin)
150-
if not relative_values:
145+
if not entitlements:
151146
binner.sort_by_ascending_sum(output)
152147

153148
if solution_filename is not None:
@@ -163,5 +158,4 @@ def optimal(
163158

164159
if __name__ == "__main__":
165160
import doctest, logging
166-
(failures, tests) = doctest.testmod(report=True, optionflags=doctest.FAIL_FAST)
167-
print("{} failures, {} tests".format(failures, tests))
161+
print(doctest.testmod(report=True, optionflags=doctest.FAIL_FAST))

0 commit comments

Comments
 (0)