Pulp LP Minimization Formulating “Select one type” Constraint

跟風遠走 提交于 2019-12-04 04:57:00

问题


The code below is used to run a LP minimization problem where we have certain foods, their nutritional value, and their cost. The code currently works in the state presented. I am trying to add one more type of constraint. I have all the foods broken up into their categories (Breakfast, Lunch, Dinner, Snack). I want to create a constraint where Only 1 Breakfast, Lunch, and Dinner item can be chosen. (No limit on snacks). The "1" and "0" correspond to if the item is (Breakfast,Lunch, Dinner, or Snack) depending on where it is in the array.

from pulp import *

Food = ["Bacon", "Eggs", "Pancakes", "Waffles", "Yogurt", "Bagels", "Sausage", "Cheerios",
    "Strawberries", "Milk", "OJ", "Oranges", "Apples", "Carrots", "Broccoli","Ham", "Turkey",
    "Steak", "Salmon", "Pasta","Chicken", "Pizza", "Rice", "Salad", "Potatoes"]

nutrition = ["Calories", "Protein", "Sugars", "Cholesterol", "Vitamin_A", "Vitamin_B", "Vitamin_C",
         "Vitamin_K", "Vitamin_E", "Zinc", "Iron", "Fat", "Sodium", "Carbs", "Fiber",
         "Calcium", "Potassium", "Folic_acid", "Thiamin"]

Category = ["Breakfast", "Lunch", "Dinner", "Snack"]

VarCategory = [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0],
               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1],
               [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
VarCategory = makeDict([Category, Food], VarCategory)


VarNutrition = [[45, 367, 84.3, 212, 250, 72.3, 150, 103, 49, 100, 134, 85.1, 52.8, 5.3, 30.9, 290, 280, 412, 159, 288, 231, 324, 428, 370, 403],
            [3, 24, 2.3, 5.3, 10.7, 2.8, 6, 3.2, 1, 8, 1, 1.3, 0.3, 1.1, 2.6, 18, 18, 21, 24.9, 12, 43.4, 13.9, 19.2, 20, 13.7],
            [0, 4, 0, 5.2, 46.7, 0, 1, 1.1, 7, 13, 23.3, 16.9, 11.1, 0.7, 1.5, 6, 5, 0, 0, 11, 0, 4.1, 13.8, 1, 0],
            [3, 86, 7, 0, 3, 2, 10, 0, 0, 3, 0, 0, 0, 0, 0, 8, 7, 61, 10, 10, 40, 9, 18, 13, 7],
            [0, 23, 2, 20, 2, 1, 4, 16, 0, 10, 2, 8, 1, 41, 11, 6, 6, 0, 2, 10, 1, 6, 41, 10, 34],
            [0, 20, 1, 19, 12, 1, 0, 27, 2, 0, 31, 2.5, 1, 1, 4, 0, 0, 50, 50, 12, 25, 10, 23, 0, 22],
            [0, 1, 1, 2, 3, 0, 0, 11, 149, 0, 62, 139, 7, 1, 135, 35, 35, 0, 0, 10, 0, 0, 17, 30, 81],
            [0, 11, 0,  0,  0,  0,  0,  1,  4,  0,  0,  0,  1,  2,  116, 0, 0,  4,  0,  10, 1,  8,  16, 0,  0],
            [0, 12, 0,  0,  0,  0,  0,  1,  2,  0,  0,  2,  0,  0,  4,  0, 0,   4,  9,  10, 2,  6,  4,  0,  0],
            [0, 15, 1,  3,  12, 1,  0,  30, 1,  0,  0,  1,  0,  0,  2,  0,  0, 69,  3,  10, 9,  10, 9,  0,  14],
            [0, 15, 4,  20, 1,  6,  4,  49, 3,  0,  2,  1,  0,  1,  4,  20, 20, 28, 6,  10, 8,  16, 7,  8,  18],
            [6, 41, 5,  11, 4,  1,  22, 3,  1,  4,  0,  1,  0,  0,  1,  8,  7, 25,  9,  20, 8,  19, 8,  29, 33],
            [6, 26, 7,  12, 6,  5,  15, 8,  0,  5,  0,  0,  0,  0,  1,  53, 42, 3,  44, 50, 4,  25, 47, 59, 20],
            [0, 2,  4,  11, 16, 5,  0,  7,  4,  4,  11, 7,  5,  0,  2,  15, 15, 0,  0,  10, 0,  13, 25, 10, 16],
            [0, 0,  0,  8,  0,  2,  0,  11, 12, 0,  2,  18, 6,  2,  9,  16, 16, 0,  0,  30, 0,  7,  10, 0,  0],
            [0, 16, 8,  4,  37, 0,  2,  11, 2,  30, 0,  8,  1,  0,  4,  6,  6, 1,   1,  0,  2,  15, 4,  4,  34],
            [0, 9,  1,  4,  14, 1,  0,  5,  7,  0,  3,  9,  3,  1,  8,  0,  0, 14,  7,  10, 10, 6,  12, 0,  41],
            [0, 17, 3,  10, 6,  6,  0,  68, 9,  0,  2,  8,  0,  1,  14, 0,  0, 5, 1,    20, 1,  0,  15, 0,  15],
            [0, 8,  5,  21, 6,  9,  0,  36, 2,  0,  63, 12, 1, 0,   4,  0,  0, 9,   2,  10, 7,  14, 17, 0,  18]]
VarNutrition = makeDict([nutrition, Food], VarNutrition)

ConstraintsLow = [2000, 72, 0, 85, 100, 100, 100, 100, 100, 0, 0, 0, 0, 90, 100, 100, 100, 100, 100]
ConstraintsLow = makeDict([nutrition],ConstraintsLow)



Cost = [1.22,   1.56,   6.79,   6.79,   1.00,   2.50,   2.00,   0.14,   1.37,   1.69, 1.99, 0.50,   0.50,   0.50,   0.50,   4.25,   4.25,   4.00,   5.00,   7.00, 3.18, 1.25,   5.00,   6.00,   3.00]
Cost = makeDict([Food], Cost)

prob = LpProblem("Nutrition Calculator", LpMinimize)

vars = LpVariable.dicts("Servings of", (Food), 0, None, LpContinuous)
Svars = LpVariable.dicts("Food Chosen", (Category, Food), 0, None, LpBinary)

prob += lpSum(vars[i]*Cost[i] for i in Food )

for j in nutrition:
    prob += lpSum([vars[i]*VarNutrition[j][i] for i in Food]) >= ConstraintsLow[j]


for i in Food:
    prob += vars[i] >= 0
    prob += vars[i] <= 2




print (prob)
prob.writeLP("Nutrition.lp")
prob.solve()
print ("Status:", LpStatus[prob.status])
for v in prob.variables():
    print (v.name, "=", v.varValue)
print ("Total Cost = ", value(prob.objective))

The problem I am having is creating such a constraint. I thought to use a binary variable but I am not sure how to implement that. Any help would be appreciated


回答1:


What you should do is have binary variables for each type of food that is selected, and then a constraint that the sum of them is 1 -- meaning exactly one of the binary variables is 1 and the others are 0.

The issue is that making, for example, the breakfast binary variable turn on means that there is an if-then condition in your linear program. If at least one breakfast item is picked, then breakfast is 1, otherwise 0. An if-then statement is not linear, so we need a clever way to make this linear. We can do this with a "big M constraint".

Make python variables representing the sum of decisions for each of types of food, e.g. breakfast_sum, lunch_sum etc.

Then make PuLP binary variables breakfast_binary, lunch_binary etc.

We'll use a big M constraint to have breakfast_binary "flip" when breakfast_sum is greater than 0. Then we'll use another constraint to make sure the sum of the binary variables <= 1.

M is basically a big number. How big does it need to be? Notice that you will never allocate more than 2 servings of each breakfast item, so try making M = 2 * {number of breakfast items}. Now look at this constraint:

M * breakfast_binary >= breakfast_sum.

If breakfast_sum is 0, then breakfast_binary is allowed to be 0. As soon as you allocate a serving of a breakfast item, breakfast_binary is forced to flip to 1.

Do this with lunch, dinner etc, then have an additional constraint that the sum of the binary variables is <= 1.

This answer paraphrases liberally from Ch 4 "Optimization Modeling" of Data Smart by John Foreman. I highly recommend it.



来源:https://stackoverflow.com/questions/43783848/pulp-lp-minimization-formulating-select-one-type-constraint

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!