python 下使用 gurobi,输出 mps,lp

    技术2022-07-17  71

    gurobi 对 python 支持的不错,我已经编写了几个规划求解的例子。每次重新编程时,之前例子里的一些知识点又忘记了,觉得很有必要总结一下。例如,下面的 python 代码调用 gurobi 求解一个简单的混合整数规划问题:

    max ⁡ x + y + 2 z s . t . x + 2 y + 3 z ≤ 4 x + y ≥ 1 x , y , z ∈ { 0 , 1 } \begin{aligned} &\max\quad & x +y +2z\\ &s.t. &\\ &&x + 2 y + 3 z \leq 4\\ && x + y \geq 1\\ && x,y, z\in\{0, 1\} \end{aligned} maxs.t.x+y+2zx+2y+3z4x+y1x,y,z{0,1}

    # This example formulates and solves the following simple MIP model: # maximize # x + y + 2 z # subject to # x + 2 y + 3 z <= 4 # x + y >= 1 # x, y, z binary from gurobipy import * try: # Create a new model m = Model("mip1") # Create variables x = m.addVar(vtype=GRB.BINARY, name="x") # default bounds for continuous type is [0, infinite] y = m.addVar(vtype=GRB.BINARY, name="y") z = m.addVar(vtype=GRB.BINARY, name="z") # Set objective m.setObjective(x + y + 2 * z, GRB.MAXIMIZE) # Add constraint: x + 2 y + 3 z <= 4 m.addConstr(x + 2 * y + 3 * z <= 4, "c0") # Add constraint: x + y >= 1 m.addConstr(x + y >= 1, "c1") m.optimize() for v in m.getVars(): print('%s %g' % (v.varName, v.x)) print('Obj: %g' % m.objVal) except GurobiError as e: print('Error code ' + str(e.errno) + ": " + str(e)) except AttributeError: print('Encountered an attribute error')

    几点总结:

    1. 创建模型

    m = Model(‘model_name’) 引号里面是自己起的模型名另一种方法,直接用 read() 读取 mps 或 lp 格式的文件。例如: model = read('test.mps')

    2. 定义求解变量

    使用 m.addVar(vtype = GRB.CONTINUOUS),小括号里面是求解变量的类型,gurobi 中 GRB.CONTINUOUS 表示大于等于零的连续型实数,GRB.INTEGER 表示整数型,GRB.BINARY 表示0-1型。小括号还可以跟 lb, ub 表示变量的上下界, 跟 obj 表示目标函数中该变量的系数(此时就不用专门再定义目标函数了)。批量定义变量可以用 m.addVars(),或者像定义数组那样使用 m.addVar(),例如: X = [m.addVar(vtype = GRB.CONTINUOUS) for t in range(3)] X = m.addVars(3, vtype = GRB.CONTINUOUS)

    上面两个命令是等价的

    3. 定义目标函数

    定义目标函数时,有时会用到 LinExpr()定义一个表达式,然后用 setObjective() 设置,例如: final_cash = LinExpr(X[0]+X[1]) # 直接 X[0]+X[1] 也可以 # Set objective m.setObjective(final_cash, GRB.MAXIMIZE)

    若目标函数的表达式比较简单,也可以直接放到 setObjective 里。

    python 中的 gurobi 会自动将变量的运算视为一个线性表达式 LinExpr,但需要更新模型,线性表达式才能在模型中生成,用到 update 函数,即: m.update() LinExpr 也可以生成数组形式,例如: I = [LinExpr() for i in range(3)]

    setObjective() 中的表达式 LinExpr 一定要在出现 setObjective() 之前定义好,若之后变动,可能会计算出错

    目标函数中不能有包含求解变量的 min 或 max 表达式(此时可以让 min 或 max 表达式等于一个辅助变量,添加到约束条件中)

    变量 var 或者线性表达式 LinExpr 可以用 python 的 sum()相加

    4. 定义约束条件

    使用 addConstr(),括号内跟约束条件即可。例如: m.addConstr(x + y <=10) 对于 min 或 max 表达式的约束条件,可以用 addGenConstrMax()、addGenConstrMin(),或者在 addConstr() 里面使用 max_()、min_()。例如: # x5 = max(x1, x3, x4, 2.0) m.addGenConstrMax(x5, [x1, x3, x4], 2.0, "maxconstr") # alternative form m.addGenConstrMax(x5, [x1, x3, x4, 2.0], name="maxconstr") # overloaded forms m.addConstr(x5 == max_([x1, x3, x4, 2.0]), name="maxconstr") m.addConstr(x5 == max_(x1, x3, x4, 2.0), name="maxconstr")

    不过我发现,当遇到数组变量时, max_ 或 min_ 可能会出错,所以一般还是用 addGenConstrMax()、addGenConstrMin() 来处理 min 或 max 表达式

    5. 设置参数

    可以通过 Params 设置,例如设置求解时间上限: m.Params.timeLimit = 100.0

    设置线性规划的求解方法:

    m.params.Method = 1 # 使用对偶单纯形法 m.params.Method = 0 # 使用原始单纯形法(迭代慢,但占内存小) m.params.Method = 2 # 使用内点法(gurobi称作barrier法)

    6. 输出变量值

    有时用 .getValue(),有时用 .X,若其中一个求解变量本质上是其他变量的表达式(LinExpr),用 getValue(),否则用 X。例如:Q.X,其中 Q 为模型中的求解变量; I.getValue(),其中 I 为求解变量 Q 的表达式。

    7. 检查约束条件

    使用 m.computeIIS() 检查不可行的约束条件(模型得不到可行解时,才能用这个函数)使用 m.feasRelax() 通过松弛最少的不可行约束条件,得到一个可行解

    8. 输出 lp, mps 等

    可以用 m.write() 输出模型,括号内可以跟多种文件格式,例如:lp,mps,或者 ilp 来输出 IIS 等。

    9. 用命令行运行

    gurobi 也支持用命令行运行,调出命令行窗口,使用 gurobi_cl 跟上不同的指令,具体可参看: https://www.gurobi.com/documentation/9.0/refman/grb_command_line_tool.html

    10. 几点注意

    gurobi 会自动调用多线程进行并行计算,所以对于大规模问题,电脑内存一定要大gurobi 官方说对于线性规划,单纯形法的计算效果一般最好。我之前一直以为内点法最好。

    转载于个人公众号:Python 数据科学与数学建模

    Processed: 0.009, SQL: 9