I am using python as a programming language and implementing a constraint of grouping similar lengths together satisfying the linear programming. Refer to the code shown below
import pulp
from itertools import product
import pandas as pd
import numpy as np
# DataFrame of item, weight, and length
df_updated = pd.DataFrame([['item1', 10, 'A'], ['item2', 20, 'B'], ['item3', 20, 'C'],
['item4', 20, 'B'], ['item5',10, 'A'], ['item6',10, 'B']],
columns = ['itemname', 'QuantityToGroup', 'Length'])
# Max weightage per bin
max_weight = 40
# Max bin to use
min_bins = int(np.ceil(round((df_updated['QuantityToGroup'].sum() / max_weight))))
max_bins = 3
problem = pulp.LpProblem("Grouping_lengths", pulp.LpMinimize)
# Variable to check, if we are using the bin or not
bin_used = pulp.LpVariable.dicts('is_bin_used', range(max_bins), lowBound=0, upBound=1, cat='Binary')
# Possible combinations to put the item in the bin
possible_item_in_bin = [(item_index, bin_num) for item_index, bin_num in product(df_updated.index, range(max_bins))]
item_in_bin = pulp.LpVariable.dicts('is_item_in_bin', possible_item_in_bin, lowBound=0, upBound=1, cat = 'Binary')
# Only one item in each bin
for item_index in df_updated.index:
problem += pulp.lpSum([item_in_bin[item_index, bin_index] for bin_index in range(max_bins)]) == 1, f"Ensure that item {item_index} is only in one bin"
# Sum of quantity grouped in each bin must be less than max weight
for bin_index in range(max_bins):
problem += pulp.lpSum(
[item_in_bin[item_index, bin_index] * df_updated.loc[item_index, 'QuantityToGroup'] for item_index in df_updated.index]
) <= max_weight * bin_used[bin_index], f"Sum of items in bin {bin_index} should not exceed max weight {max_weight}"
# Length Constraints
lengths = list(df_updated.Length.unique())
for length in lengths:
items_n = df_updated.index[df_updated['Length'] == length].tolist()
if len(items_n) > 1:
for bin in range(max_bins - 1):
first_index = items_n[0]
for item in items_n[1:]:
constr = pulp.LpConstraint(item_in_bin[first_index, bin] - item_in_bin[item, bin], sense = 0, rhs = 0, name = f"place item {item} in bin {bin} if length number {length} is chosen for this bin")
problem += constr
# Objective function to minimize bins used
problem += pulp.lpSum(bin_used[bin_index] for bin_index in range(max_bins)), "Objective: Minimize Bins Used"
problem.solve(pulp.PULP_CBC_CMD(msg = False))
for val in problem.variables():
if val.varValue == 1:
print(val.name, val.varValue)
For the given input code is unable to group items of length B as the total weight for length B is (item 2 -> 20, item 4 -> 20, and item 6 -> 10) 50 which is greater than max weight 40. The code is working as expected.
But I have to make the length constraint elastic, which means it is okay to violate the constraint but the penalty should be added if the constraint is violated. I have explored Elastic Constraints which I think are exactly for my kind of requirement.
But I am facing an issue to implement them holding the linearity of the problem. Do I have to formulate my constraint in a different manner? Any help is appreciated.
Possible expected Output from the code making sure the objective of minimizing the wastage is respected and constraint is followed. If the constraint is not followed then the penalty is added.
# item 1 (A - 10), item 5 (A - 10), item3 (C - 20) on 1st bin.
# item 2 (B) and item 4 (B) on 2nd bin.
# item 6 (B - 10) on 3rd bin
I have also tried alternative ways to formulate the length constraint section as shown below:
# Length Variable
lengths = list(df_updated.length.unique())
# Possible combinations to put the lengths in the bin
possible_length_in_bin = [(length, bin_num) for length, bin_num in product(range(len(lengths)), range(max_bins))]
# Constraint to group similar lengths together on same bin
length_in_bin = pulp.LpVariable.dicts('LengthInBin', possible_length_in_bin, cat = 'Binary')
for item, length, bins_index in product(df_updated.index, range(len(lengths)), range(max_bins)):
problem += pulp.lpSum(item_in_bin[(item, bins_index)] == length_in_bin[(length, bins_index)]), (f"Only place item {item} in bin {bins_index} if length number {length} is chosen for this bin")
The rest of the section remains the same as above. But still, the solution doesn't return desired results.