You need better variable and constraint names, and you have only one constraint (the exclusive vendor selection per row):
import pandas as pd
import pulp
def make_assignment(suffix: str) -> pulp.LpVariable:
return pulp.LpVariable(name='asn_' + suffix, cat=pulp.LpBinary)
def constrain_rows(row: pd.Series) -> pd.Series:
names = (row.name + '_' + row.index).to_series(index=row.index)
vars = names.apply(make_assignment)
prob.addConstraint(name='excl_' + row.name, constraint=1 == pulp.lpSum(vars))
return vars
df = pd.DataFrame({
'John': (6.33, 6.76, 6.84),
'Chris': (6.62, 6.62, 6.62),
'Paul1': (6.37, 6.37, 6.34),
'Paul2': (6.52, 6.76, 6.20),
}, index=pd.Index(name='Dress', data=('Shirt', 'Trouser', 'Jacket')))
prob = pulp.LpProblem(name='dress_vendors', sense=pulp.LpMinimize)
assigns = df.apply(func=constrain_rows, axis=1)
prob.objective = pulp.lpDot(assigns.values.ravel(), df.values.ravel())
print(prob)
prob.solve()
_, used = assigns.applymap(pulp.LpVariable.value).values.nonzero()
for dress, vendor in zip(df.index, df.columns[used]):
print(f'{dress} bought from {vendor}')
print(f'Dress total is ${prob.objective.value():.2f}')
dress_vendors:
MINIMIZE
6.62*asn_Jacket_Chris + 6.84*asn_Jacket_John + 6.34*asn_Jacket_Paul1 + 6.2*asn_Jacket_Paul2 + 6.62*asn_Shirt_Chris + 6.33*asn_Shirt_John + 6.37*asn_Shirt_Paul1 + 6.52*asn_Shirt_Paul2 + 6.62*asn_Trouser_Chris + 6.76*asn_Trouser_John + 6.37*asn_Trouser_Paul1 + 6.76*asn_Trouser_Paul2 + 0.0
SUBJECT TO
excl_Shirt: asn_Shirt_Chris + asn_Shirt_John + asn_Shirt_Paul1
+ asn_Shirt_Paul2 = 1
excl_Trouser: asn_Trouser_Chris + asn_Trouser_John + asn_Trouser_Paul1
+ asn_Trouser_Paul2 = 1
excl_Jacket: asn_Jacket_Chris + asn_Jacket_John + asn_Jacket_Paul1
+ asn_Jacket_Paul2 = 1
VARIABLES
0 <= asn_Jacket_Chris <= 1 Integer
0 <= asn_Jacket_John <= 1 Integer
0 <= asn_Jacket_Paul1 <= 1 Integer
0 <= asn_Jacket_Paul2 <= 1 Integer
0 <= asn_Shirt_Chris <= 1 Integer
0 <= asn_Shirt_John <= 1 Integer
0 <= asn_Shirt_Paul1 <= 1 Integer
0 <= asn_Shirt_Paul2 <= 1 Integer
0 <= asn_Trouser_Chris <= 1 Integer
0 <= asn_Trouser_John <= 1 Integer
0 <= asn_Trouser_Paul1 <= 1 Integer
0 <= asn_Trouser_Paul2 <= 1 Integer
Welcome to the CBC MILP Solver
Version: 2.10.3
Build Date: Dec 15 2019
command line - .venv/lib/python3.10/site-packages/pulp/solverdir/cbc/linux/64/cbc /tmp/1eacd290c9f44c86a43b0722be3104de-pulp.mps timeMode elapsed branch printingOptions all solution /tmp/1eacd290c9f44c86a43b0722be3104de-pulp.sol (default strategy 1)
At line 2 NAME MODEL
At line 3 ROWS
At line 8 COLUMNS
At line 57 RHS
At line 61 BOUNDS
At line 74 ENDATA
Problem MODEL has 3 rows, 12 columns and 12 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Continuous objective value is 18.9 - 0.00 seconds
Cgl0004I processed model has 0 rows, 0 columns (0 integer (0 of which binary)) and 0 elements
Cbc3007W No integer variables - nothing to do
Cuts at root node changed objective from 18.9 to -1.79769e+308
Probing was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Gomory was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Knapsack was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Clique was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
MixedIntegerRounding2 was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
FlowCover was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
TwoMirCuts was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
ZeroHalf was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Result - Optimal solution found
Objective value: 18.90000000
Enumerated nodes: 0
Total iterations: 0
Time (CPU seconds): 0.00
Time (Wallclock seconds): 0.00
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00
Shirt bought from John
Trouser bought from Paul1
Jacket bought from Paul2
Dress total is $18.90