Matlab调用Cplex的二三事

最近一个师弟来探讨为何问题feasible但是有一类等式约束明显不满足的问题,细说起来大概是前一天还是infeasible,不知动了什么突然有解了,不经大喜,冷静下来定睛一看发现解有问题这样一个过程,不经回想起曾经的我,把求解优化问题归为玄学。打了几年的交道,虽然还没有深入敌人内部,但也是积累了一些应对小技巧,借此机会总结分享一下~

在具体案例分析之前还是想客观的说一句,对于包括本人在内的一些不太了解求解器算法的外行来说,常常蜜汁自信,将无法理解的bug归为玄学,事实是永远不要轻易怀疑求解器,毕竟它已经是一个相对成熟的求解器了==绝大多数时候都是因为自己不经意间的一些错误或不规范操作导致的。

下面开始干货:

情形一:问题infeasible(exitflag=-2)

这个应该是第一次满怀信心把问题丢给求解器后碰到的最常见的情形。绝大多数情况都是由于模型本身存在冲突约束,或者自己敲的时候手误导致。

1、按照模型约束的物理意义进行初步推导,检查参数设置是否合理,是否可能导致可行域为空。

2、模型本身没有问题,手误导致可太正常了,这个时候先不要慌,首先要在保持模型变量与约束结构完整的前提下,把问题降维到最小。然后可以借助两种方法:

1)cplex.writeModel('model.lp')导出模型lp文件,大概长下面这个样子,截了一部分,小问题可以通过此方法逐一检查丢给求解器的模型是不是自己想的那样。

\ENCODING=ISO-8859-1
\Problem name: CPLEX

Minimize
 obj: x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15
      + x16 + x17 + x18 + x19 + x20 + x21 - x22 - x23 - x24 - x25 - x26 - x27
      - x28 - x29 - x30 - x31 - x32 - x33 - x34 - x35 - x36 - x37 - x38 - x39
      - x40 - x41
Subject To
 c1:   2 x42 - x108 <= 0
 c2:   4 x43 - x109 <= 0
 c3:   - x42 + x110 <= 0
 c4:   - 3 x43 + x111 <= 0 。。。
Bounds
 1 <= x1 <= 4
 0 <= x2 <= 1
 0 <= x3 <= 1
 0 <= x4 <= 1 。。。
Binaries
 x2  x3  x4  x5  x6  x7  x8  x9  x10  x11  x12  x13  x14  x15  x16  x17  x18 
 x19  x20  x21  x22  x23  x24  x25  x26  x27  x28  x29  x30  x31  x32  x33 
 x34  x35  x36  x37  x38  x39  x40  x41  x42  x43  x74  x75  x76  x77  x78 
 x79  x80  x81  x82  x83 
End

2)cplex.writeConflict('conflict.clp')运用冲突检测功能,类似的可以导出冲突部分的模型,它会告诉你哪条约束不满足,以及所涉及变量的其他约束、上下限定义等。

if cplex.Solution.status == 103
     cplex.refineConflict();
     cplex.writeConflict('conflict.clp');
 end

\ENCODING=ISO-8859-1
\Problem name: CPLEX_conflict

Minimize
 obj:
Subject To
 c9:   - x44 + x74 <= 0
 c14:  - x49 + x79 <= 0
 c15:  - x50 + x80 <= 0。。。
\Sum of equality rows in the conflict:
\ sum_eq: - x42 + x44 + x49 + x50 + x51 + x52 + x53 - x59 - x60 - x61 - x62
\         - x63 + 2 x64 + 2 x69 + 2 x70 + 2 x71 + 2 x72 + 2 x73 - 6 x74 - 5 x75
\         - 5 x76 - 5 x77 - 5 x78 - 5 x79 - 4 x80 - 3 x81 - 2 x82 - x83  = 0
Bounds
 0 <= x42 <= 1   
 0 <= x74 <= 5。。。
Binaries
 x42  x74  x75  x76  x77  x78  x79  x80  x81  x82  x83 
End

可以看出要想快速找出infeasible的原因, 原问题规模太大是很不友好的, 建议对于有明显结构特征的问题, 养成从小算例开始调试再扩展的好习惯~

情形2: 问题feasible, 但exitflag=5

这个是个进阶情形了,在含二次约束的较复杂问题中比较容易碰到。 好容易跑出了解, 先别高兴得太早看看标志位是不是1( 毕竟即使是1解都有可能有问题==),5的意思是重要方向导数小于规定的容许范围并且约束违背小于options TolCom, 说人话我理解就是可能有一些约束不满足。如果所得的解在重要约束上是满足精度要求的那么roughly可以接受,否则这个解还是没有办法用。

1)诶那我直观地想我把tolerance调小一点行不行,但发现我碰到的问题这个不太好使。

opt = cplexoptimset('cplex');
opt.feasopt.tolerance=1e-9;(默认1e-6)

2)既然我改变不了求解器的内部算法逻辑,那只能通过改变我丢给它的这个玩意儿看看是否能躲过这个坑(即用魔法打败魔法)。对于等式约束,可以尝试在可接受范围内进行松弛,然后以罚函数形式加入目标函数中(与原目标函数值量级最好相差两个量级左右),罚函数同样也适用于不等式约束的松弛(即哪里不满足强调哪里)。

情形3:问题feasible,exitflag=1,欸但是有约束不满足!

这种时候是大家最容易相信玄学的时候……表面上看似乎很不科学,作为外行的我至今不太能解释这种情况出现的原因,因此应对措施也只能case by case见招拆招,比如我之前有个问题中把自变量的范围由[-1,1]调整为[0,1],等式约束就满足了。似乎求解器在识别正负、等与不等存在着本质区别。另外还有可能是数据截断问题导致,说到这里就不得不强调下求解过程中需要注意模型的数值问题,在一个模型中尽量不要使得不同变量的数量级差太多(标幺大法保平安,不要轻易搞出很大或者很小的数,可行域会比较畸形),运用大M法进行松弛的时候,M的取值也需注意,一般高出2~3个数量级即可,无需任性的设置为1e100这样……

此外做一些别的小总结,常见报错:

1)CPLEX Error  3019: Failure to solve MIP subproblem.求解混合整数规划问题中遇到数值问题,注意下变量的量级

2)exitflag=-8这种诡异结果可能是内存问题……

3)CPLEX Error 5002: Q is not positive semi-definite.求解二次规划问题中二次项系数矩阵Q非半正定问题,首先检查下是否问题本身就不是凸的,不是的话出门左转不送(Cplex、Gurobi只能求解凸规划),Xpress和matlab自带的fmincon可以求解一些更一般的问题。另外将二阶锥约束sqrt(xTAx)<=y改为二次约束xTAx-y^2<=0,要对y加上约束y>=0,这样Q才不会报非半正定的错误(二阶锥是一种特殊的凸约束但Q的确不是半正定)

Cplex的调用方式:

1)通过中介公司yalmip,这个我一直没用过……据说比较人性化还是比较推荐。

2)暴力赋值系数矩阵,以MIQCP问题为例,有三种方法:

(1)

cplex = Cplex();
cplex.Model.sense = 'minimize';
cplex.Model.obj   = f;
cplex.Model.lb    = lb;
cplex.Model.ub    = ub;
cplex.Model.l    = l;
cplex.Model.Q    = Q;
cplex.Model.r    = r;
cplex.Model.ctype = ctype';
cplex.Model.A =  [Aineq;Aeq];
cplex.Model.lhs = [-inf*ones(rineq,1); 0*ones(req,1)];
cplex.Model.rhs = [bineq;beq];
cplex.solve();
if cplex.Solution.status == 103
    cplex.refineConflict();
    cplex.writeConflict('conflict.clp');
end
x=cplex.Solution.x;

注意这种写法如果等式约束常数项beq不为0,cplex会新增若干变量表示beq,但这些变量取值不是等于beq而是松弛到0<=x<=beq,导致解不正确,所以只能把这部分等式约束改为不等式约束。

(2)

problem.H=[];
problem.f=f;
problem.Aineq=Aineq;
problem.bineq=bineq;
problem.Aeq=Aeq;
problem.beq=beq;
problem.qc.a=l;
problem.qc.rhs=r;
problem.qc.Q=Q;
problem.sos=[];
problem.lb=lb;
problem.ub=ub;
problem.ctype=ctype';
problem.options = cplexoptimset('diagnostics','on','mip.tolerances.mipgap',0, 'mip.tolerances.absmipgap',0);%参数设置
[x,fval,exitflag,~]= cplexmiqcp(problem);

(3)

opt = cplexoptimset('cplex');%参数设置
opt.feasopt.tolerance=1e-9;

opt.ExportModel = 'model.lp';

[x,fval,exitflag]=cplexmiqcp([],f,Aineq,bineq,Aeq,beq,l,Q,r,[],[],[],lb,ub,ctype',[],opt);

贴一些Cplex的一些常用参数设置, 具体参考https://www.ibm.com/docs/zh/icos/12.10.0?topic=cplex-parameters,学会看官方文档, 其实很多问题都可以从中找到解答。

比如设置参数a=b

写法(1)

cplex.Param.a=b

写法(2)

problem.options = cplexoptimset('a','b'/b);

写法(3)

options = cplexoptimset('cplex');

options.a=b;

常用参数:

timelimit求解时间

mip.tolerances.mipgap整数规划问题的求解gap(默认0.0001,一般可以放大一些)

今天先写到这里,内容有错误或不严谨的地方还请各路大神多多批评指正, 本人也在不断学习优化中~

  • 9
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值