coin-or / python-mip

Python-MIP: collection of Python tools for the modeling and solution of Mixed-Integer Linear programs
Eclipse Public License 2.0
514 stars 89 forks source link

Wrong solution in a simple MIP for number partitioning #267

Closed erelsgl closed 2 years ago

erelsgl commented 2 years ago

Describe the bug I wrote a simple MIP for the following problem: given a set of numbers, partition them into two subsets such that the smallest sum is as large as possible. The returned solution is clearly wrong.

To Reproduce

Here is the code I used:

import mip

items = [11,11,11,11,22]
bins = [0,1]
model = mip.Model("partition")
counts: dict = {
    item: [model.add_var(var_type=mip.INTEGER) for bin in bins] for item in items
}  # The variable counts[i][j] determines how many times item i appears in bin j.
bin_sums = [
    sum([counts[item][bin] * item for item in items]) for bin in bins
]

# Construct the list of constraints:
counts_are_non_negative = [counts[item][bin] >= 0 for bin in bins for item in items]
each_item_in_one_bin = [
    sum([counts[item][bin] for bin in bins]) == 1 for item in items
]
bin_sums_in_ascending_order = [bin_sums[1] >= bin_sums[0]]

for constraint in counts_are_non_negative + each_item_in_one_bin + bin_sums_in_ascending_order: 
        model += constraint

model.objective = mip.maximize(bin_sums[0])

# Solve the ILP:
model.verbose = 1
status = model.optimize()
print("status: ", status)

# Construct the output:
for bin in bins:
    for item in items:
        count_item_in_bin = int(counts[item][bin].x)
        if count_item_in_bin>0:
            print(f"bin {bin} contains item {item}")

Expected behavior The optimal partition is clearly [11, 11, 11], [11, 22], since the smallest sum in that case is 33, which is optimal.

But the returned partition is [11, 11, 11, 11], [22].

Desktop (please complete the following information): Windows 10. Python 3.9. MIP 1.13.0.

rschwarz commented 2 years ago

I think the way you construct the counts dictionary does not work as expected, because there are multiple identical item values, which replace the previous entries. Compare this snippet:

>>> items = [11, 11, 11, 12]
>>> d = {item: i for i, item in enumerate(items)}
>>> d
{11: 2, 12: 3}
erelsgl commented 2 years ago

You are right. Thanks!