全文来自运筹OR帷幄公众号,归运筹OR帷幄所有。

编者按

Gurobi 启动优化后,用户可能会看到这样的提示信息" Model is infeasible or unbounded",意味着模型或者无解,或者无界。用户可以设置参数 DualReductions = 0,然后再运行优化,就会得到明确的信息。如果不可行(infeasible),则可以运行 model.computeIIS() 函数获得冲突的约束条件,再运行 model.write("abc.ilp")命令将冲突的约束输出到 abc.ilp 文件中进行后续分析。如果是无界的,那么可以检查变量和约束的上下界设置,是否有可能出现无界情况。对于连续模型,可以设置 参数 InfUnbdInfo = 1 来获得连续模型的 UnbdRay。

(一)利用feasRelaxS() 和 feasRelax() 函数

对于复杂的问题,运行computeIIS() 的时间可能会很长,或者输出的 ILP 冲突约束文件不容易看出来具体的错误源头。这时候用户可以考虑采用Gurobi的feasRelaxS() 和 feasRelax() 函数来提供协助。其中,feasRelaxS() 是feasRelax() 函数的简化版,更容易使用,我们以此为列来进行说明。感兴趣的用户可以详细阅读参考手册中这二个函数的说明,以及参考安装目录/examples/python 下的 feasopt.py 范例。

feasRelaxS() 和 feasRelax() 函数的功能,是在用户原有模型基础上,为变量和约束添加松弛变量,让松弛模型变得可行,然后通过最小化松弛量来获得一个最小违反变量和约束限制的模型。用户可以观察优化后的松弛变量的数值是否为0(全部松弛变量为0意味着模型有可行解),以及数值范围,来初步了解哪些约束可能是问题的源头,以及冲突的程度。需要说明的是,任何模型不可能由单一约束或者单一变量边界造成不可行。不可行的模型往往存在若干个约束和变量边界一起发生冲突,导致最终不可行,因此导致冲突的原因可以有无穷多的解读和因果链条,并不存在唯一的化解方式,需要结合模型的应用场景来调整。

例如一个模型不可行,在运行 ComputeIIS() 之后,输出为如下的 ILP 文件:

 \ Model mip1 \ LP format - for model browsing. Use MPS format to capture full model detail. Minimize   Subject To  R2016: C1126 >= 0  R2023: C1129 <= 1  R4042: C2722 >= 0  R4049: C2725 <= 1  R6068: C4318 >= 0  R6075: C4321 <= 1  R8094: C5914 >= 0  R8101: C5917 <= 1  R10165: 0.031640625 C66 + 0.002734375 C1126 - 0.002734375 C1129    + 0.0023125 C2722 - 0.0023125 C2725 + 0.00175 C4318 - 0.00175 C4321    + 0.001 C5914 - 0.001 C5917 + C8069 = -0.6 Bounds  C66 >= -1  C1126 free  C1129 free  C2722 free  C2725 free  C4318 free  C4321 free  C5914 free  C5917 free End

假设这个文件名叫 model.ilp。分析具体冲突的原因,对于这个小规模问题来说,也不是一眼就可以看出来。但我们可以启动 Gurobi 的交互环境,来进一步分析这个问题。

例如一个模型不可行,在运行 ComputeIIS() 之后,输出为如下的 ILP 文件:

Gurobi 交互环境下,我们可以

(1)用 m=read("model.ilp") 读入这个模型。ILP 格式和 LP 格式一致。
(2)用 m.feasRelaxS(0,False,False,True) 添加松弛变量。函数参数的具体含义请看参考手册。
(3)用 m.optimize() 运行这个松弛模型。
(4)用 m.write("model.sol") 将松弛模型的优化结果输出到 model.sol 文件。
(5)用 m.write("model.lp") 将松弛模型输出到 model.lp 文件,这里面包含了松弛变量,以Art开头。所有的松弛变量为非负。如下:

 \ LP format - for model browsing. Use MPS format to capture full model detail. Minimize   ArtP_R2016 + ArtN_R2023 + ArtP_R4042 + ArtN_R4049 + ArtP_R6068    + ArtN_R6075 + ArtP_R8094 + ArtN_R8101 + ArtP_R10165 + ArtN_R10165 Subject To  R2016: C1126 + ArtP_R2016 >= 0  R2023: C1129 - ArtN_R2023 <= 1  R4042: C2722 + ArtP_R4042 >= 0  R4049: C2725 - ArtN_R4049 <= 1  R6068: C4318 + ArtP_R6068 >= 0  R6075: C4321 - ArtN_R6075 <= 1  R8094: C5914 + ArtP_R8094 >= 0  R8101: C5917 - ArtN_R8101 <= 1  R10165: 0.002734375 C1126 - 0.002734375 C1129 + 0.0023125 C2722    - 0.0023125 C2725 + 0.00175 C4318 - 0.00175 C4321 + 0.001 C5914    - 0.001 C5917 + 0.031640625 C66 + C8069 + ArtP_R10165 - ArtN_R10165    = -0.6 Bounds  C1126 free  C1129 free  C2722 free  C2725 free  C4318 free  C4321 free  C5914 free  C5917 free  C66 >= -1 End​​​​​

(6)打开 model.sol 文件,我们注意到松弛变量 ArtN_R10165 的数值不为0,对应的R10165约束中另外一个松弛变量ArtP_R10165为0,就意味着约束R10165 距离可行还相差 0.5605625。化解这个冲突可以有很多方法,要结合模型实际应用背景来处理,例如可以让C8069的下界从原来的0, 更改为-0.5605625;或者更改 C66 的边界值,等等。

 # Objective value = 0.5605625 C1126 0 C1129 1 C2722 0 C2725 1 C4318 0 C4321 1 C5914 0 C5917 1 C66 -1 C8069 0 ArtP_R2016 0 ArtN_R2023 0 ArtP_R4042 0 ArtN_R4049 0 ArtP_R6068 0 ArtN_R6075 0 ArtP_R8094 0 ArtN_R8101 0 ArtP_R10165 0 ArtN_R10165 0.5605625

我们以上的方法是将feasRelaxS() 函数作用于ILP 文件,也就是运行ComputeIIS()之后的输出文件。用户也可以直接将feasRelaxS()作用于原始LP文件,方法一致。作用于ILP的好处就是排除不相关的约束,让分析更聚焦在冲突的约束上。

(二)利用 IIS Force 属性

Gurobi 9.5 版本允许用户为变量的上下界、线性约束、二次约束、SOS约束、非线性约束单独设置 IIS Force 属性,也就是 IISConstrForce, IISLBForce, IISUBForce, IISSOSForce, IISQConstrForce, and IISGenConstrForce 属性。

如果这些属性值设置为1(默认值为-1),那么就要求Gurobi必须将这些变量上下界和约束包含到 IIS计算当中。这个属性的最大作用,就是当一个可行模型在添加新的约束和变量上下界之后,变得不可行,需要溯源哪些新的变化造成了模型不可行。

这种情况下,用户可以将原模型中的变量边界和约束的 Force 属性设定为1,然后运行ComputeIIS(),那么Gurobi 只会专注在新增加的约束和变量边界上,不但溯源的时间会更短,而且提供的信息可以更容易让用户发现问题。但需要注意这种方式获得的IIS集合不一定是最小集合。

(三)通过约束增减来判断

对复杂问题的不可行进行溯源,是一项繁琐的工作,计算量不亚于求解一个优化模型。如果以上方式无法在短时间找到源头,用户也可以通过逐步增加约束,边增加边判断的方式发现问题的源头。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐