Gurobi教程-从入门到入土-一篇顶万篇

再详尽的帮助文档也不如举几个示例能让人看得明白,于是想通过一个帖子涵盖Gurobi的所有操作。

安装与激活

软件下载、免学术ip申请学术许可,参见 Gurobi中国官方网站。具体过程简单且网上很多教程。值得注意的是当运行pycharm时发现Gurobi的license过期,在重新安装 gurobi.lic 时若选择的地址不是系统环境变量,需要在系统环境变量中添加该.lic,变量名为“GRB_LICENSE_FILE”,并重启电脑

版权

以下引自官网:

GUROBI 为中国学校教师、学生提供半年免费使用版本(可延续),功能没有限制,需要符合以下规定。同时激活时的机器IP地址将会被记录,如果存在违反以下规定的情况,将有可能面临不利后果。
(1)在中国高等教育机构的教师或者在籍学生(研究所不在此列。中科院系统需要具备中科院大学证件);
(2)用于非商业用途;
(3)学术许可只能申请本人使用,为其他人申请将会造成申请人和使用人同时失去使用学术许可的永久资格;
(4)学术许可严禁安装和使用在非学术科研的场合和设备上;
(5)学生和教师严禁在企业和不符申请资格的科研机构上安装和使用学术许可;
(6)企业和科研机构违规安装和使用学术许可,将会被追究经济和法律责任;
(7)使用Gurobi 的科研论文的第一作者需要具有 Gurobi 正版许可(合规学术许可或者正版商业许可);
(8)遵守其他版权规定 http://www.gurobi.com/pdf/eula/eula.pdf

其中第(7)条需要格外注意。

MIP模型

举个例子

例如求解VRP问题。

import numpy as np
import matplotlib.pyplot as plt
from gurobipy import *

rnd = np.random
rnd.seed(1) # 随机种子,如有本行,则程序每次运行结果一样。可任意赋值

n = 10 # 一共几个客户点/城市/需求点
xc = rnd.rand(n+1)*200 # 随机生成每个城市的横坐标,范围[0,200]
yc = rnd.rand(n+1)*100 # 随机生成每个城市的纵坐标,范围[0,100]

# 可以画图看一眼生成的城市什么样子
# plt.plot(xc[0], yc[0], c='r',marker='s' ) # 索引为0的点,即depot/仓库/出发点
# plt.scatter(xc, yc, c='b') # 客户点

N = list(range(1,n+1)) # 客户点集合
V = list(range(0,n+1)) # 所有点集合(仓库+客户点)
A = [(i,j) for i in V for j in V if i !=j] # 城市之间有哪些连线/路段/弧段
C = {(i,j): np.hypot(xc[i]-xc[j], yc[i]-yc[j]) for i,j in A} # np.hypot:二范数=求平方和;计算弧段的长度
Q = 20 # 车最大载重
q = {i:rnd.randint(1,10) for i in N} # 随机生成客户点的需求量,范围[1,10]

mdl = Model('CVRP') # 起名字

x = mdl.addVars(A, vtype=GRB.BINARY) # 增加变量xij,表示是否链接ij
u = mdl.addVars(N, vtype=GRB.CONTINUOUS) # 增加变量ui,表示车在该点处累计载货量

mdl.modelSense = GRB.MINIMIZE # 目标为最小化
mdl.setObjective(quicksum(x[i,j]*C[i,j] for i,j in A )) # 目标函数为总距离

# 添加所有约束
mdl.addConstrs(quicksum(x[i,j] for j in V if i != j)==1 for i in N) 
mdl.addConstrs(quicksum(x[i,j] for i in V if i!=j)==1 for j in N)
mdl.addConstrs((x[i,j]==1) >> 
               (u[i] + q[j] == u[j]) for i,j in A if i!=0 and j!=0)
mdl.addConstrs(u[i] >= q[i] for i in N)
mdl.addConstrs(u[i] <= Q for i in N)

mdl.optimize() # 优化

#优化完成,下面输出结果
active_arts = [a for a in A if x[a].x > 0.9] # 输出最优解的所有连线,即xij中是1的(i,j)
# 由于存在误差,xij可能为0.999999999,因此不要用==1
print(active_arts)

# 画图
for index, (i,j) in enumerate(active_arts):
    plt.plot([xc[i],xc[j]],[yc[i],yc[j]],c='r')
plt.plot(xc[0], yc[0], c='g',marker='s' )
plt.scatter(xc, yc, c='b')

输出的啥

求解某VRP模型后,输出结果如下:

第一部分

可以不看。

Gurobi Optimizer version 9.1.2 build v9.1.2rc0 (win64)
Thread count: 8 physical cores, 16 logical processors, using up to 16 threads
Optimize a model with 40 rows, 120 columns and 220 nonzeros
Model fingerprint: 0x56854507

第二部分

总结了模型的规模。包括有几个约束条件,几个变量。Coefficient statistics暂时不知道什么意思。

Model has 90 general constraints
Variable types: 10 continuous, 110 integer (110 binary)
Coefficient statistics:
  Matrix range     [1e+00, 1e+00]
  Objective range  [1e+01, 2e+02]
  Bounds range     [1e+00, 1e+00]
  RHS range        [1e+00, 2e+01]

第三部分

Gurobi求解MIP的presolve阶段。

Presolve added 171 rows and 8 columns
Presolve time: 0.01s
Presolved: 211 rows, 128 columns, 1318 nonzeros
Variable types: 38 continuous, 90 integer (90 binary)

第四部分

展示求解过程,其中1

Root relaxation: objective 3.612672e+02, 40 iterations, 0.00 seconds
    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0  361.26720    0   21          -  361.26720      -     -    0s
H    0     0                    1130.6043611  361.26720  68.0%     -    0s
H    0     0                    1108.0315407  361.26720  67.4%     -    0s
H    0     0                     899.3261060  361.26720  59.8%     -    0s
H    0     0                     773.2318022  361.26720  53.3%     -    0s
H    0     0                     748.6243052  361.26720  51.7%     -    0s
H    0     0                     696.3684124  361.26720  48.1%     -    0s
H    0     0                     689.4096665  366.86305  46.8%     -    0s
     0     0  370.85380    0   22  689.40967  370.85380  46.2%     -    0s
H    0     0                     663.9889500  370.85380  44.1%     -    0s
     0     0  375.47206    0   22  663.98895  375.47206  43.5%     -    0s
     0     0  375.47206    0   22  663.98895  375.47206  43.5%     -    0s
     0     0  378.67819    0   22  663.98895  378.67819  43.0%     -    0s
     0     0  378.67819    0   22  663.98895  378.67819  43.0%     -    0s
     0     2  378.67819    0   21  663.98895  378.67819  43.0%     -    0s
H   31    40                     611.6880568  395.45082  35.4%  20.0    0s

Nodes(1-2列):Gurobi在利用 branch-and-cut(分支剪界法)求解MIP时的过程。第1列:已搜索过的节点个数。第2列:还没有被搜索的叶子节点。第1列会越来越多,第2列数字可能偶尔减小。已搜索节点数(第1列)总是为0的话,表示Gurobi MIP solver正在处理根节点。在根节点需要花时间产生cutting plane,以及尝试多种heuristic方法来减小后面branch-and-cut tree的规模。当某行前面标有 H 或 *,表示找到了一个新的可行解。H 和 *分别表示该可行解是通过 heuristic 或 branching 方法找到的。

Current Node(3-5列):branch-and-cut tree中被搜索过的特定节点的信息。第3列:相应relaxation(松弛)的目标值。第4列:branch-and-cut tree中节点的深度。第5列:非整数值的整数变量的个数。

Objective Bounds(6-8列):关于可行解的区间信息。对最小化问题来说,第6列:上界(目前找到的最优解)。第7列:下界(线性松弛后的最优解,由于某些把某些变量松弛为连续值,因此一定是不可行解,但全局的可行的最优解不会比该松弛解更优)。第8列:Gap((上界-下界)/上界)。

Work(9-10列):求解运算的工作量。第9列:branch-and-cut tree中每个节点simplex迭代的平均次数,Number of iterations/node。第10列:模型求解时间。

第五部分

Cutting planes:
  Learned: 4
  Gomory: 7
  Cover: 17
  Implied bound: 18
  MIR: 5
  GUB cover: 2
  Zero half: 2
  Relax-and-lift: 4
  
Explored 2398 nodes (36083 simplex iterations) in 0.23 seconds
Thread count was 16 (of 16 available processors)

第六部分

最重要最关心的部分。
Solution count:共找到几个可行解,分别是什么

Solution count 9: 611.688 663.989 689.41 ... 1130.6
Optimal solution found (tolerance 1.00e-04)
Best objective 6.116880568072e+02, best bound 6.116880568072e+02, gap 0.0000%

求解状态

查看模型求解状态:

mdl.status

返回一个int,常见的状态代码如下。完整版见官方手册

Status codeValueDescription
OPTIMAL2已找到最优解
INFEASIBLE3模型无解
TIME_LIMIT9到达用户设定时限,终止寻优

隐藏的坑

python数据存储误差

有时为了能够将模型线性化,需要借助大M法。通常将M设为一个很大量级的数,如1e+6等。但在有些情况下,M过大将导致一些错误。python存储数据时可能存在误差,即使是0-1变量,优化结束后可能并不是整数0或1,而是3.3306690738754696e-6或0.99999879454,此时乘以一个较大的M时,量级将回到一个不可忽略的范围,导致约束条件判断错误。
如在路径规划问题中消除子环时,M本应可以是越大越好的正数,但实际操作时仍推荐找到M的比较紧的上界2。同理,代码中的M0也是如此。

mdl.addConstrs(miu[d, i, j, m] - miu[d, i, j, n] + M * y[d,i,j,m,n] 
			   <= M - 1 + M0 * x[j,n]
			   for d in D for i in y_i_set for j in y_j_set if j > i for m in V for n in V)

同时这个特性导致我们想查找结果为1的变量时,最好不要写x[i].X==1,而是x[i].X>0.9。

NotImplementedError

出现这个错误的一种可能是,添加约束条件时选用了“>”或"<",Gurobi不允许绝对大于或绝对小于的约束。

额外技巧

设置%gap或求解时间限制

面对大规模问题,不指望精确算法能够给出全局最优解,因此设置参数以控制运算时间。

  • %gap=|BP−BF|/|BP|, 其中 BP 为线性松弛后得到的解,BF 则为当前最优的可行解。就是输出结果中 Objective Bounds 的 Gap 值。3
  • TimeLimit,求解时限。

设置方法4

# 在调用 model.optimize() 之前进行如下设置。 MIPGap 和 TimeLimit 的单位分别是分数和秒。 
mdl.Params.MIPGap = 0.05    # %gap = 5%; 或 model.setParam('MIPGap', 0.05)
mdl.Params.TimeLimit = 300  # TimeLimit = 5 minutes; 或 model.setParam('Timelimit', 300)

关闭输出信息

有时gurobi只负责算法中的一部分,不希望看到它打印出的信息。
设置方法5

mdl.setParam('OutputFlag', 0)

删除决策变量或约束条件

设置方法6

mdl.remove(model.getVars()[0]) # 删除第一个变量
mdl.remove(model.getVars()[0:10])
mdl.remove(model.getVars())  # 删除全部变量
mdl.remove(model.getConstrs()[0])  #删除第一个约束
mdl.remove(model.getConstrs()[1:3])
mdl.remove(model.getConstrs())  # 删除全部约束
mdl.remove(model.getQConstrs()[0])
mdl.remove(model.getSOSs()[0])
mdl.remove(model.getGenConstrs()[0])

模型修改之后记得更新

mdl.update()

  1. https://blog.csdn.net/weixin_33467739/article/details/112110951 ↩︎

  2. https://www.zhihu.com/question/42175410 ↩︎

  3. https://blog.csdn.net/qinghai5068/article/details/51443851 ↩︎

  4. https://stackoom.com/question/4LZjl ↩︎

  5. https://blog.csdn.net/weixin_46991173/article/details/110497320 ↩︎

  6. gurobi官方文档 ↩︎

  • 67
    点赞
  • 340
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Python Gurobi教程是一个旨在帮助学习者快速上手使用Gurobi优化工具的教程Gurobi是一款功能强大的数学优化库,它提供了一套丰富的接口和功能,可以用于解决各种复杂的数学优化问题。 这个教程主要分为以下几个部分: 第一部分是介绍Gurobi的安装和配置。学习者需要根据自己的操作系统选择合适的版本,并按照安装指南进行安装。同时,还需要获取一个合法的许可证才能使用Gurobi。 第二部分是PythonGurobi的基础知识。学习者需要了解Python的基本语法和数据结构,并且学习如何使用GurobiPython接口。这部分内容包括如何定义优化模型、添加变量和约束、设置目标函数等。 第三部分是Gurobi的高级功能。学习者将学习如何使用Gurobi的各种高级功能来解决实际的优化问题。这些功能包括设置求解器参数、导入和导出模型、处理不等式约束等。 第四部分是案例分析和实践。学习者将通过一些实际的案例和练习来巩固所学的知识。这些案例和练习将涵盖不同领域的优化问题,如资源分配、路径规划等。 通过学习Python Gurobi教程,学习者可以掌握使用Gurobi求解数学优化问题的基本方法和技巧。同时,他们还可以了解到如何利用Gurobi的高级功能来提高解决问题的效率和准确性。这将为他们在学术研究和实际应用中解决复杂的优化问题提供有力的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值