Integer Linear Programming - Pulp Guide

Integer Linear Programming - Pulp Guide

PuLP is a linear programming modeling tool written in Python. PuLP can generate MPS or LP files and call GLPK, COIN-OR CLP/CBC, CPLEX, GUROBI, MOSEK, XPRESS, CHOCO, MIPCL, HiGHS, SCIP/FSCIP, etc. to solve linear problems.

Official document address: https://coin-or.github.io/pulp/index.html

The standard representation of linear programming is as follows:

(LP) min ⁡ c T x , s . t .   A x ≤ b , x ∈ R + n \text{(LP)}\quad\begin{aligned} &\min c^Tx,\\ &s.t.\ Ax\le b,\\ &\qquad x\in \R_+^n \end{aligned} (LP)mincTx,s.t. Axb,xR+n

1. Introduction

Use pip install pulpinstall pulp.

from pulp import *

You can use to LpVariable()create new variables, for example, create variables 0 ≤ x ≤ 3 0\le x\le 30x3

x = LpVariable("x", 0, 3)

Create variable 0 ≤ y ≤ 1 0\le y\le 10y1

y = LpVariable("y", 0, 1)

LpProblem()Create a new problem using , say, myProblema linear programming problem called:

prob = LpProblem("myProblem", LpMinimize)

Combine variables into expressions and constraints and add them to the problem:

prob += x + y <= 2

If you add an expression (instead of a constraint), it becomes the objective function:

prob += -4*x + y

Solve using the default solver:

status = prob.solve()

Show the status of the solution:

LpStatus[status]
'Optimal'

View specific values:

print(value(x))
print(value(y))
2.0
0.0

2. Configure the solver

PuLP usually has multiple ways of connecting solvers. Depending on the connection method, the method of configuring the connection will be different. We can summarize integrations into two broad categories:

  1. Use the solver's command line interface.

  2. Python library using solvers.

Not all solvers have Python libraries, but most have command line interfaces. If the name of the solver API ends with CMD (e.g. PULP_CBC_CMD, CPLEX_CMD, GUROBI_CMD, etc.), then it is the command line, otherwise it is a python library.

Here I am installing cplex. You can go to the IBM official website to apply for installation: https://www.ibm.com/analytics/cplex-optimizer .

The configuration path is as follows:

# 这里需要换成自己电脑上cplex.exe所在的地址
path_to_cplex = r'D:\software\cplex\cplex\bin\x64_win64\cplex.exe'
import pulp as pl
model = pl.LpProblem("Example", pl.LpMinimize)
solver = pl.CPLEX_CMD(path=path_to_cplex)
_var = pl.LpVariable('a')
_var2 = pl.LpVariable('a2')
model += _var + _var2 == 1
result = model.solve(solver)

Once configured once, there is no need to configure it again.

import pulp as pl
model = pl.LpProblem("Example", pl.LpMinimize)
solver = pl.CPLEX_CMD()
_var = pl.LpVariable('a')
_var2 = pl.LpVariable('a2')
model += _var + _var2 == 1
result = model.solve(solver)
print(result)
1

3. Case study - cat food problem

Problem Description

Uncle Ben's wanted to produce their cat food products at the lowest possible cost while ensuring they met the nutritional analysis requirements shown on the can. Therefore, they wanted to vary the amount of each ingredient used (primary ingredients include chicken, beef, lamb, rice, wheat and gelatin) while still meeting nutritional standards .

Insert image description here

Chicken, beef and lamb cost $0.013, $0.008 and $0.010 respectively, while rice, wheat and gelatin cost $0.002, $0.005 and $0.001 respectively. (All costs are per gram.) For this exercise, we will ignore the vitamin and mineral ingredients. (These costs can be very small.)

Each ingredient contributes to the total weight of protein, fat, fiber and salt in the final product. The contribution of ingredients per gram (in grams) is shown in the table below:

Stuff Protein Fat Fibre Salt
Chicken 0.100 0.080 0.001 0.002
Beef 0.200 0.100 0.005 0.005
Mutton 0.150 0.110 0.003 0.007
Rice 0.000 0.010 0.100 0.002
Wheat bran 0.040 0.010 0.150 0.008
Gel 0.000 0.000 0.000 0.000

modeling representation

Determine decision variables

Let's say Whiskas wants to make their cat food with two ingredients: chicken and beef. First define our decision variables:

x 1 = % chicken used in a can of cat food x 2 = % beef used in a can of cat food x_1 = % chicken used in a can of cat food x_2 = % beef used in a can of cat foodx1=Percentage of chicken used in a can of cat foodx2=Percentage of beef used in a can of cat food

These variables must be greater than zero.

Formulate objective function

The objective function becomes:

min ⁡ 0.013 x 1 + 0.008 x 2 \min 0.013x_1+0.008x_2 min0.013x1+0.008x2

limit

The variables must add up to 100 and meet nutritional requirements:

1.000 x 1 + 1.000 x 2 = 100.0 0.100 x 1 + 0.200 x 2 ≥ 8.0 0.080 x 1 + 0.100 x 2 ≥ 6.0 0.001 x 1 + 0.005 x 2 ≤ 2.0 0.002 x 1 + 0.005 x 2 ≤ 0.4 \begin{aligned} 1.000x_1&+1.000x_2=100.0\\ 0.100x_1&+0.200x_2\ge 8.0\\ 0.080x_1&+0.100x_2\ge 6.0\\ 0.001x_1&+0.005x_2\le 2.0\\ 0.002x_1&+0.005x_2\le 0.4 \end{aligned} 1.000x10.100x10.080x10.001x10.002x1+1.000x2=100.0+0.200x28.0+0.100x26.0+0.005x22.0+0.005x20.4

python implementation

Import the library:

from pulp import *

Use the LpProblem function to define a variable named prob. It takes two parameters, the first is an arbitrary name for this problem (as a string), the second parameter depends on the type of LP being solved, either LpMinimize or LpMaximize:

prob = LpProblem("WhiskasModel", LpMinimize)

Use LpVariableclasses to create problem variables x1and x2. It has four parameters, the first is an arbitrary name representing this variable, the second is the lower bound of this variable, the third is the upper bound, and the fourth is the data type (discrete or continuous). The options for the fourth parameter are LpContinuousor LpIntegerand its default value is LpContinuous. If we are modeling the number of cans to produce, we need input LpIntegerbecause it is discrete data. The bounds can be entered directly as a number, or as , Nonewhich means there is no bound (i.e., plus or minus infinity), and the default value is None. Create two variables suitable for this problem:

# 本问题中的鸡肉和牛肉变量
x1 = LpVariable("ChickenPercent",0 , None, LpInteger)
x2 = LpVariable("BeefPercent",0 , None, LpInteger)

Add objective function:

# 首先添加目标函数
prob += 0.013 * x1 + 0.008 * x2, "每罐猫粮的成本价格"

Add restrictions:

# The five constraints are entered
prob += x1 + x2 == 100, "百分比之和"
prob += 0.100 * x1 + 0.200 * x2 >= 8.0, "蛋白质需求"
prob += 0.080 * x1 + 0.100 * x2 >= 6.0, "脂肪需求"
prob += 0.001 * x1 + 0.005 * x2 <= 2.0, "纤维需求"
prob += 0.002 * x1 + 0.005 * x2 <= 0.4, "盐分需求"

Use writeLP()the function to copy this information to a .lpfile located in the directory where your code block runs. Once the code runs successfully, you can open the file with a text editor .lpand see what the above steps are doing:

# 将问题数据写入.lp文件中
prob.writeLP('WhiskasModel.lp')
[BeefPercent, ChickenPercent]

The LP problem is solved using the solver selected by PuLP, in which case the solve()following input brackets are empty, but they can be used to specify the solver to be used (e.g., prob.solve(CPLEX())):

prob.solve(CPLEX())
1

Output the solution result, which may be "Not Solved", "Infeasible", "Unbounded", "Undefined" or "Optimal":

print("Status:", LpStatus[prob.status])
Status: Optimal

View the value of each variable:

for v in prob.variables():
    print(v.name, "=", v.varValue)
BeefPercent = 66.0
ChickenPercent = 34.0

Output the value of the objective function:

print("每罐猫粮的原材料成本为:", value(prob.objective))
每罐猫粮的原材料成本为: 0.97

complete formula

Now we will fully formulate the problem using all variables. While it is possible to add something to the above approach and implement it into Python, we will see a better approach that does not mix problem data and formulation. This will make it easier to change any problematic data for other tests. We'll start by defining the problem algebraically:

  1. Determine the decision variable, which is the percentage of the different ingredients we include in the jar. Since the jar weighs 100g, these percentages also represent the grams of each ingredient contained. Note that these percentages must be between 0 and 100.

    x 1 = % chicken used in a can of cat food x 2 = % beef used in a can of cat food x 3 = % lamb used in a can of cat food x 4 = % rice used in a can of cat food x 5 = % of bran used in a can of cat food x 6 = % of gel used in a can of cat food x_1 = % of chicken used in a can of cat food \\ x_2 = % of beef used in a can of cat food \\ x_3=% of lamb used in a can of cat food\\ x_4=% of rice used in a can of cat food\\ x_5=% of wheat bran used in a can of cat food\\ x_6=% of gel used in a can of cat food percentage ofx1=Percentage of chicken used in a can of cat foodx2=Percentage of beef used in a can of cat foodx3=Percentage of lamb used in a can of cat foodx4=Percentage of rice used in a can of cat foodx5=Percentage of wheat bran used in a can of cat foodx6=Percentage of gel used in a can of cat food

  2. Formulate the objective function. For the Whiskas cat food problem, the goal is to minimize the total cost of the cat food ingredients per can.

    min ⁡ 0.013 x 1 + 0.008 x 2 + 0.010 x 3 + 0.002 x 4 + 0.005 x 5 + 0.001 x 6 \min 0.013x_1+0.008x_2+0.010x_3+0.002x_4+0.005x_5+0.001x_6 min0.013x1+0.008x2+0.010x3+0.002x4+0.005x5+0.001x6

  3. Establish constraints. The constraints for the Whiskas cat food problem are:

    • The sum of the percentages must account for the entire jar (=100%).

    • Meet prescribed nutritional analysis requirements.

    The constraints for "whole tank" are:

    x 1 + x 2 + x 3 + x 4 + x 5 + x 6 = 100 x_1+x_2+x_3+x_4+x_5+x_6=100 x1+x2+x3+x4+x5+x6=100

    To meet nutritional analysis requirements, we need at least 8g protein, 6g fat, but no more than 2g fiber and 0.4g salt per 100g. To formulate these constraints, we utilize previous contribution tables for each component. This allows us to formulate the following constraints on the total contribution of protein, fat, fiber and salt from the ingredients: 0.100
    x 1 + 0.200 x 2 + 0.150 x 3 + 0.000 x 4 + 0.040 x 5 + 0.0 x 6 ≥ 8.0 0.080 x 1 + 0.100 x 2 + 0.110 x 3 + 0.010 x 4 + 0.010 x 5 + 0.0 x 6 ≥ 6.0 0.001 x 1 + 0.005 x 2 + 0.003 x 3 + 0.100 x 4 + 0.150 x 5 + 0.0 x 6 ≤ 2.0 0. 002x 1 + 0.005 x 2 + 0.007 x 3 + 0.002 x 4 + 0.008 x 5 + 0.0 x 6 ≤ 0.4 \begin{aligned} 0.100x_1&+0.200x_2+0.150x_3+0.000x_4+0.040x_5+0.0x_6≥8.0\\ 0.080 x_1&+0.100x_2+0.110x_3+0.010x_4+0.010x_5+0.0x_6≥6.0\\ 0.001x_1&+0.005x_2+0.003x_3+0.100x_4+0.150x_5+0.0x_6≤2.0\\ 0.002x_1& +0.005x_2+0.007x_3+ 0.002x_4+0.008x_5+0.0x_6≤0.4 \end{aligned}0.100x10.080x10.001x10.002x1+0.200x2+0.150x3+0.000x4+0.040x5+0.0x _68.0+0.100x2+0.110x3+0.010x4+0.010x5+0.0x _66.0+0.005x2+0.003x3+0.100x4+0.150x5+0.0x _62.0+0.005x2+0.007x _3+0.002x4+0.008x5+0.0x _60.4

python implementation
from pulp import *
# 原材料
Ingredients = ["CHICKEN", "BEEF", "MUTTON", "RICE", "WHEAT", "GEL"]

# 成本字典
costs = {
    
    
    "CHICKEN": 0.013,
    "BEEF": 0.008,
    "MUTTON": 0.010,
    "RICE": 0.002,
    "WHEAT": 0.005,
    "GEL": 0.001,
}

# 蛋白质字典
proteinPercent = {
    
    
    "CHICKEN": 0.100,
    "BEEF": 0.200,
    "MUTTON": 0.150,
    "RICE": 0.000,
    "WHEAT": 0.040,
    "GEL": 0.000,
}

# 脂肪字典
fatPercent = {
    
    
    "CHICKEN": 0.080,
    "BEEF": 0.100,
    "MUTTON": 0.110,
    "RICE": 0.010,
    "WHEAT": 0.010,
    "GEL": 0.000,
}

# 纤维字典
fibrePercent = {
    
    
    "CHICKEN": 0.001,
    "BEEF": 0.005,
    "MUTTON": 0.003,
    "RICE": 0.100,
    "WHEAT": 0.150,
    "GEL": 0.000,
}

# 盐分字典
saltPercent = {
    
    
    "CHICKEN": 0.002,
    "BEEF": 0.005,
    "MUTTON": 0.007,
    "RICE": 0.002,
    "WHEAT": 0.008,
    "GEL": 0.000,
}
# 创建问题
prob2 = LpProblem("The Whiskas Problem", LpMinimize)

Create a ingredient_varsdictionary named LP variables whose lower bound is defined as zero. The reference key of the dictionary is the raw material name, and the data is Ingr_IngredientName. (Example: MUTTON: Ingr_MUTTON)

ingredient_vars = LpVariable.dicts("Ingr", Ingredients, 0)

Since costsand ingredient_varsare now dictionaries with ingredient names as reference keys, the data can be easily extracted using list comprehensions

# 目标函数
prob2 += (
    lpSum([costs[i] * ingredient_vars[i] for i in Ingredients]),
    "Total Cost of Ingredients per can",
)
# 五个限制
prob2 += lpSum([ingredient_vars[i] for i in Ingredients]) == 100, "百分比之和"
prob2 += (
    lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0,
    "蛋白质需求",
)
prob2 += (
    lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0,
    "脂肪需求",
)
prob2 += (
    lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0,
    "纤维需求",
)
prob2 += (
    lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4,
    "盐分需求",
)
# 将问题数据写入.lp文件中
prob2.writeLP('WhiskasModel2.lp')
[Ingr_BEEF, Ingr_CHICKEN, Ingr_GEL, Ingr_MUTTON, Ingr_RICE, Ingr_WHEAT]
prob2.solve(CPLEX())
1
print("求解状态:", LpStatus[prob2.status])
求解状态: Optimal
for v in prob2.variables():
    print(v.name, "=", v.varValue)
Ingr_BEEF = 60.0
Ingr_CHICKEN = 0.0
Ingr_GEL = 40.0
Ingr_MUTTON = 0.0
Ingr_RICE = 0.0
Ingr_WHEAT = 0.0
print("每罐猫粮的原材料成本为:", value(prob2.objective))
每罐猫粮的原材料成本为: 0.52

おすすめ

転載: blog.csdn.net/weixin_47692652/article/details/132193424