Add step size to a linear optimization

I'm working on a blending problem similar to the pulp example

I have this constrain to make sure the quantity produced is the desired one

prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) == 64, "KGRequirement"

But I also need to add another constraint for the minimun value different than zero, this is because is not convenient that I take for example, 0.002KG of one ingredient, I have to take either 0 or at least 2 kg, hence valid cases are e.g. 0, 2, 2.3, 6, 3.23.

I tried to make it this way:

for i in deposit:
    prob += (KG[i] * deposit_vars[i] == 0) or (TM[i] * deposit_vars[i] >= 30)

But that is not working and it just make the problem Infeasible

EDIT

This my current code:

import pulp
from pulp import *
import pandas as pd

food = ["f1","f2","f3","f4"]
KG = [10,20,50,80]
Protein =       [18,12,16,18]
Grass = [13,14,13,16]
price_per_kg =  [15,11,10,22]

##            protein,carbohydrates,kg

df = pd.DataFrame({"tkid":food,"KG":KG,"Protein":Protein,"Grass":Grass,"value":price_per_kg})


deposit =  df["tkid"].values.tolist()

factor_volumen = 1



costs =  dict((k,v) for k,v in zip(df["tkid"],df["value"]))
Protein =  dict((k,v) for k,v in zip(df["tkid"],df["Protein"]))
Grass =  dict((k,v) for k,v in zip(df["tkid"],df["Grass"]))
KG =  dict((k,v) for k,v in zip(df["tkid"],df["KG"]))

prob = LpProblem("The Whiskas Problem", LpMinimize)
deposit_vars = LpVariable.dicts("Ingr",deposit,0)
prob += lpSum([costs[i]*deposit_vars[i] for i in deposit]), "Total Cost of Ingredients per can"

#prob += lpSum([deposit_vars[i] for i in deposit]) == 1.0, "PercentagesSum"
prob += lpSum([Protein[i] *KG[i] * deposit_vars[i] for i in deposit]) >= 17.2*14, "ProteinRequirement"
prob += lpSum([Grass[i] *KG[i] * deposit_vars[i] for i in deposit]) >= 12.8*14, "FatRequirement"
prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) == 14, "KGRequirement"
prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) <= 80, "KGRequirement1"

prob.writeLP("WhiskasModel.lp")
prob.solve()
# The status of the solution is printed to the screen
print ("Status:", LpStatus[prob.status])

# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
    print (v.name, "=", v.varValue)

# The optimised objective function value is printed to the screen
print ("Total Cost of Ingredients per can = ", value(prob.objective))

The new contrain I want to add is in this part:

prob += lpSum([KG[i] * deposit_vars[i] for i in deposit]) <= 80, "KGRequirement1"

Where I want the product KG[i] * deposit_vars[i] be either 0 or to be between a and b

Upvotes: 1

Views: 800

Answers (2)

Erwin Kalvelagen
Erwin Kalvelagen

Reputation: 16742

'or' is not something you can use in an LP / MIP model directly. Remember, an LP/MIP consists of a linear objective and linear constraints.

To model x=0 or x≥L you can use socalled semi-continuous variables. Most advanced solvers support them. I don't believe Pulp supports this however. As a workaround you can also use a binary variable δ:

δ*L ≤ x ≤ δ*U

where U is an upperbound on x. It is easy to see this works:

δ = 0   ⇒   x = 0
δ = 1   ⇒   L ≤ x ≤ U 

Semi-continuous variables don't require these constraints. Just tell the solver variable x is semi-continuous with bounds [L,U] (or just L if there is no upperbound).

The constraint

a*x=0   or   L ≤ a*x ≤ U

can be rewritten as

δ*L ≤ x*a ≤ δ*U
δ binary variable

This is a fairly standard formulation. Semi-continuous variables are often used in finance (portfolio models) to prevent small allocations.

All of this keeps the model perfectly linear (not quadratic), so one can use a standard MIP solver and a standard LP/MIP modeling tool such as Pulp.

Upvotes: 2

Manuel J. Diaz
Manuel J. Diaz

Reputation: 1270

In the traditional linear programming formulation, all variables, objective function(s), and constraints need to be continuous. What you are asking is how to make this variable a discrete variable, i.e. it can only accept values a,b,... and not anything in between. When you have a combination of continuous and discrete variables, that is called a mixed integer problem (MIP). See PuLP documentation that reflects this explanation. I suggest you carefully read the blending problems mentions on "integers;" they are scattered about the page. According to PuLP's documentation, it can solve MIP problems by calling external MIP solver, some of which are already included.

Without a minimum working example, it is a little tricky to explain how to implement this. One way to do this would be to specify the variable(s) as an integer with the values it can take as a dict. Leaving the default solver, COIN-OR's CBC solver solver, will then solve the MIP. Meanwhile, here's a couple of resources for you to move forward:

Upvotes: 2

Related Questions