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
541 stars 96 forks source link

Problem is infeasible unexpectedly with using MIP-CBC #248

Open msperr opened 2 years ago

msperr commented 2 years ago

I created a Bin Packing Problem with a quite small number of items. Then I added symmetry constraints in order to solve the problem faster. Unfortunately, the problem gets infeasible although it should be feasible. I'm using MIP 1.13.0 with Python 3.10.1 on Windows.

I added my code for reproducing the problem. Thanks for your help!

from typing import Dict

import mip

def bin_packing(weights: Dict[int, float], capacity: float, symmetry_constraints: bool) -> mip.OptimizationStatus:
    bins = list(range(4))
    model = mip.Model('Bin Packing Problem')
    model.verbose = 0
    x = {(i, j): model.add_var(f'x({i},{j})', var_type=mip.BINARY) for i in weights.keys() for j in bins}
    y = {j: model.add_var(f'y({j})', obj=1, var_type=mip.BINARY) for j in bins}
    load = {j: mip.xsum(x[(i, j)] * w for i, w in weights.items()) for j in bins}
    for j in bins:
        model += load[j] <= y[j] * capacity, f'Capacity({j})'
    for i in weights.keys():
        model += mip.xsum(x[(i, j)] for j in bins) == 1, f'Item({i})'
    if symmetry_constraints:
        for j1, j2 in [(j1, j2) for j1 in bins for j2 in bins if j1 < j2]:
            model += load[j1] >= load[j2], f'Symmetry({j1},{j2})'
    return model.optimize()

if __name__ == '__main__':
    weights = {0: 307, 1: 191, 2: 87.4, 3: 465.5339, 4: 273, 5: 106, 6: 385}
    print(bin_packing(weights, 480, True))
    print(bin_packing(weights, 480, False))
aphi commented 2 years ago

If you're looking for a workaround, CBC will solve this model fine with the symmetry constraints instead specified in an ordered line rather than all-pairs.

    if symmetry_constraints:
        for j1, j2 in zip(bins, bins[1:]):
            model += load[j1] >= load[j2], f'Symmetry({j1},{j2})'

To the question of why it doesn't work as defined, it's a bit peculiar but seems to relate to the floating point coefficients. You can save the model to an lp file, then immediately read it back, and that will also solve. The issue could be in the Python<->C++ interface, or in CBC itself. @h-g-s may be the best person to diagnose.