I have a basic knapsack problem where I need to fit the most weight possible in a bin:
import pyomo.environ as pyo
w = {'hammer': 5, 'wrench': 7, 'screwdriver': 4, 'towel': 3}
limit = 14
M = pyo.ConcreteModel()
M.ITEMS = pyo.Set(initialize=w.keys())
M.x = pyo.Var(M.ITEMS, within=pyo.Binary)
M.value = pyo.Objective(expr=sum(M.x[i] for i in M.ITEMS), sense=pyo.maximize)
M.weight = pyo.Constraint(expr=sum(w[i] * M.x[i] for i in M.ITEMS) <= limit)
solver = pyo.SolverFactory("cbc")
solver.solve(M)
M.x.display()
Which works fine:
Key : Lower : Value : Upper : Fixed : Stale : Domain
hammer : 0 : 1.0 : 1 : False : False : Binary
screwdriver : 0 : 1.0 : 1 : False : False : Binary
towel : 0 : 1.0 : 1 : False : False : Binary
wrench : 0 : 0.0 : 1 : False : False : Binary
My problem is that I need to include a value component and I must ensure that the average value of all selected items is above a certain fixed threshold. I implemented it like this:
v = {'hammer': 1, 'wrench': 3, 'screwdriver': 1, 'towel': 2}
w = {'hammer': 5, 'wrench': 7, 'screwdriver': 4, 'towel': 3}
limit = 14
M = pyo.ConcreteModel()
M.ITEMS = pyo.Set(initialize=w.keys())
M.x = pyo.Var(M.ITEMS, within=pyo.Binary)
M.value = pyo.Objective(expr=sum(M.x[i] for i in M.ITEMS), sense=pyo.maximize)
M.weight = pyo.Constraint(expr=sum(w[i] * M.x[i] for i in M.ITEMS) <= limit)
M.above_average = pyo.Constraint(
expr=sum(M.x[i] * v[i] for i in M.ITEMS) / sum(M.x[i] for i in M.ITEMS) >= 2
)
solver = pyo.SolverFactory("cbc")
solver.solve(M)
M.x.display()
But that caused the following error:
ValueError: Cannot write legal LP file. Constraint 'above_average' has a body with nonlinear terms.
I understand that the constraint that I added is non-linear, therefore the error message is correct. My question is how I could solve that with pyomo (or equivalent) or workaround this problem.