I have found this code for a bin packing problem in Google OR-tools. https://developers.google.com/optimization/bin/bin_packing#python_3 However, it does not have a volume constraint. What if the total quantity/number of items packed in a bin (which is printed in the output of the program) is only limited to 2, what code should be added? Re: the volume of the items are all the same, items just have different weights
from ortools.linear_solver import pywraplp
def create_data_model():
"""Create the data for the example."""
data = {}
weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30]
data['weights'] = weights
data['items'] = list(range(len(weights)))
data['bins'] = data['items']
data['bin_capacity'] = 100
return data
def main():
data = create_data_model()
# Create the mip solver with the SCIP backend.
solver = pywraplp.Solver.CreateSolver('SCIP')
# Variables
# x[i, j] = 1 if item i is packed in bin j.
x = {}
for i in data['items']:
for j in data['bins']:
x[(i, j)] = solver.IntVar(0, 1, 'x_%i_%i' % (i, j))
# y[j] = 1 if bin j is used.
y = {}
for j in data['bins']:
y[j] = solver.IntVar(0, 1, 'y[%i]' % j)
# Constraints
# Each item must be in exactly one bin.
for i in data['items']:
solver.Add(sum(x[i, j] for j in data['bins']) == 1)
# The amount packed in each bin cannot exceed its capacity.
for j in data['bins']:
solver.Add(
sum(x[(i, j)] * data['weights'][i] for i in data['items']) <= y[j] *
data['bin_capacity'])
# Objective: minimize the number of bins used.
solver.Minimize(solver.Sum([y[j] for j in data['bins']]))
status = solver.Solve()
if status == pywraplp.Solver.OPTIMAL:
num_bins = 0.
for j in data['bins']:
if y[j].solution_value() == 1:
bin_items = []
bin_weight = 0
for i in data['items']:
if x[i, j].solution_value() > 0:
bin_items.append(i)
bin_weight += data['weights'][i]
if bin_weight > 0:
num_bins += 1
print('Bin number', j)
print(' Items packed:', bin_items)
print(' Total weight:', bin_weight)
print()
print()
print('Number of bins used:', num_bins)
print('Time = ', solver.WallTime(), ' milliseconds')
else:
print('The problem does not have an optimal solution.')
if __name__ == '__main__':
main()
OUTPUT OF THE PROGRAM:
Bin number 0
Items packed: [1, 5, 10]
Total weight: 87
Bin number 1
Items packed: [0, 6]
Total weight: 90
Bin number 2
Items packed: [2, 4, 7]
Total weight: 97
Bin number 3
Items packed: [3, 8, 9]
Total weight: 96
Number of bins used: 4
Add a constraint to not exceed a volume capacity.
# The amount packed in each bin cannot exceed its volume capacity.
for j in data['bins']:
itm_vol = [x[(i, j)] * data['volumes'][i] for i in data['items']]
solver.Equation(solver.sum(itm_vol) <= y[j] * data['bin_volume'])
It is also possible to specify how many items (or range) are in each bin.
# Each bin must contain 1 or 2 items.
for j in data['bins']:
items = [x[(i, j)] for i in data['items']]
solver.Equation(solver.sum(items) <= 2)
solver.Equation(solver.sum(items) >= 1)
Here is a translation of the problem to Gekko that gives the same answer as the original problem statement.
from gekko import GEKKO
def create_data_model():
"""Create the data for the example."""
data = {}
weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30]
data['weights'] = weights
data['items'] = list(range(len(weights)))
data['bins'] = data['items']
data['bin_capacity'] = 100
return data
def main():
data = create_data_model()
solver = GEKKO(remote=False)
# Variables
# x[i, j] = 1 if item i is packed in bin j.
x = {}
for i in data['items']:
for j in data['bins']:
x[(i, j)] = solver.Var(lb=0, ub=1, integer=True)
# y[j] = 1 if bin j is used.
y = {}
for j in data['bins']:
y[j] = solver.Var(lb=0, ub=1, integer=True)
# Constraints
# Each item must be in exactly one bin.
for i in data['items']:
items = [x[(i, j)] for j in data['bins']]
solver.Equation(sum(items) == 1)
# The amount packed in each bin cannot exceed its capacity.
for j in data['bins']:
bin_cap = [x[(i, j)] * data['weights'][i] for i in data['items']]
solver.Equation(sum(bin_cap) <= y[j] * data['bin_capacity'])
# Objective: minimize the number of bins used.
solver.Minimize(sum([y[j] for j in data['bins']]))
solver.options.SOLVER=1
solver.solve(disp=False)
if solver.options.APPSTATUS == 1:
num_bins = 0.
for j in data['bins']:
if y[j].value[0] == 1:
bin_items = []
bin_weight = 0
for i in data['items']:
if x[i, j].value[0] > 0:
bin_items.append(i)
bin_weight += data['weights'][i]
if bin_weight > 0:
num_bins += 1
print('Bin number', j)
print(' Items packed:', bin_items)
print(' Total weight:', bin_weight)
print()
print()
print('Number of bins used:', num_bins)
print('Time = ', solver.options.SOLVETIME, ' seconds')
else:
print('The problem does not have an optimal solution.')
if __name__ == '__main__':
main()
Here is a modified version with the added constraints.
from gekko import GEKKO
def create_data_model():
"""Create the data for the example."""
data = {}
weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 31]
volumes = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1]
data['weights'] = weights
data['volumes'] = volumes
data['items'] = list(range(len(weights)))
data['bins'] = data['items']
data['bin_capacity'] = 100
data['bin_volume'] = 2
return data
def main():
data = create_data_model()
solver = GEKKO(remote=False)
solver.solver_options = ['minlp_gap_tol 0.5',\
'minlp_maximum_iterations 10000',\
'minlp_max_iter_with_int_sol 500',\
'minlp_branch_method 1',\
'minlp_integer_leaves 1'\
'minlp_max_iter_with_int_sol 1000']
# Variables
# x[i, j] = 1 if item i is packed in bin j.
x = {}
for i in data['items']:
for j in data['bins']:
x[(i, j)] = solver.Var(lb=0, ub=1, integer=True)
# y[j] = 1 if bin j is used.
y = {}
for j in data['bins']:
y[j] = solver.Var(lb=0, ub=1, integer=True)
# Constraints
# Each item must be in exactly one bin.
for i in data['items']:
item = [x[(i, j)] for j in data['bins']]
solver.Equation(solver.sum(item) == 1)
# The amount packed in each bin cannot exceed its weight capacity.
for j in data['bins']:
itm_wgt = [x[(i, j)] * data['weights'][i] for i in data['items']]
solver.Equation(solver.sum(itm_wgt) <= y[j] * data['bin_capacity'])
# The amount packed in each bin cannot exceed its volume capacity.
for j in data['bins']:
itm_vol = [x[(i, j)] * data['volumes'][i] for i in data['items']]
solver.Equation(solver.sum(itm_vol) <= y[j] * data['bin_volume'])
# Each bin must contain at most 2 items.
for j in data['bins']:
items = [x[(i, j)] for i in data['items']]
solver.Equation(solver.sum(items) <= 2)
# Objective: minimize the number of bins used.
solver.Minimize(solver.sum([y[j] for j in data['bins']]))
solver.options.SOLVER=1
solver.solve(disp=False)
if solver.options.APPSTATUS == 1:
num_bins = 0.
for j in data['bins']:
if y[j].value[0] == 1:
bin_items = []
bin_weight = 0
for i in data['items']:
if x[i, j].value[0] > 0:
bin_items.append(i)
bin_weight += data['weights'][i]
if bin_weight > 0:
num_bins += 1
print('Bin number', j)
print(' Items packed:', bin_items)
print(' Total weight:', bin_weight)
print()
print()
print('Number of bins used:', num_bins)
print('Time = ', solver.options.SOLVETIME, ' seconds')
else:
print('The problem does not have an optimal solution.')
if __name__ == '__main__':
main()
Here is the solution:
Bin number 3
Items packed: [2, 7]
Total weight: 61
Bin number 4
Items packed: [1]
Total weight: 30
Bin number 6
Items packed: [5, 10]
Total weight: 58
Bin number 8
Items packed: [3, 4]
Total weight: 72
Bin number 9
Items packed: [0, 6]
Total weight: 90
Bin number 10
Items packed: [8, 9]
Total weight: 60
Number of bins used: 6.0
Time = 6.3572 seconds
You may need to adjust the constraints or the objective to get the answer that you need.