JAVA for Cplex(更新版)
一、Cplex的介绍
Cplex是一种专门用来求解大规模线性规划问题的求解工具。不仅仅是LP问题,对于二次规划 QP,二次有约束规划QCP,混合整数线性规划MIP问题,甚至Network Flow问题,都是很
好的求解工具。官网https://www.ibm.com/cn-zh/products/ilog-cplex-optimization-studio中有关于Cplex简单的介绍。Cplex的优势在于:
1、 能解决一些非常困难的行业问题;
2、 求解速度非常快。
3、 在Cplex的支持下,使得matlab对于大规模的问题,以及线性规划的效率都有很多大的提升。
二、Cplex的使用环境
本文中以eclipse为开发环境,代码的是使用java语言。
三、Cplex的简单案例。
3.1 Cplex求解器的构成。
先看一个官网给的例子。
代码:
'''
public class MyTest {
/**
* obj = x1 + 5x2 + 1x3
* -x1 + x2 + x3 <=56;
* x1 - 3x2 + x3 <= 39;
* 0 <= x1 <= 40;
* 0 <= x2, x3
* @throws IloException
*/
public static void main(String[] args) throws IloException {
IloNumVarType varType = IloNumVarType.Float;
int varNum = 3;
IloCplex cplex = new IloCplex();
//决策变量申明
IloNumVar [] vars = new IloNumVar[varNum];
double[] xishu = new double[]{1,5,1};
double[] mins = new double[]{0,0,0};
double[] maxs = new double[]{30,Double.MAX_VALUE,Double.MAX_VALUE};
for (int i = 0; i < vars.length; i++) {
vars[i] = cplex.numVar(mins[i], maxs[i],varType);
}
//目标函数
cplex.addMaximize(cplex.scalProd(vars, xishu));
//约束条件
cplex.addLe(cplex.sum(cplex.prod(1.0, vars[0]), cplex.prod(1.0, vars[1]), cplex.prod(1.0,vars[2])), 56);
cplex.addLe(cplex.sum(cplex.prod(1.0, vars[0]), cplex.prod(-3.0, vars[1]), cplex.prod(1.0,vars[2])), 39);
if(cplex.solve()){
cplex.output().println("Solution status = " + cplex.getStatus());
cplex.output().println("Solution value = " + cplex.getObjValue());
double[] val = cplex.getValues(vars);
int ncols = cplex.getNcols();
for (int j = 0; j < ncols; ++j)
cplex.output().println("Column: " + j + " Value = " + val[j])
}
cplex.end();}
运行结果:
以上代码实现了求目标函数在不等式约束条件下的最优解。由运行结果可以看出该线性规划问题的最优值是280,最优解x1,x2,x3的值分别是0,56,0。
总结一下,Cplex求解器的构成主要有三大部分:(1)决策变量的定义;(2)目标函数的形式;(3)约束条件的表达。这三部分是我们需要关注的重点,如果正确地表达出来,那
么我们便能利用Cplex求解任何的线性规划问题。简单地介绍一下,代码中IloNumVar [] vars = new IloNumVar[varNum];这里定义的是一个连续变量的数组,数组大小为
varNum。数组中存放的是决策变量x1,x2,x3. cplex.addMaximize()方法用于输入目标函数的形式,比如obj = 1x1 + 5x2 + 1*x3,我们可以这样表达
cplex.addMaximize(cplex.scalProd(vars, xishu));其中,cplex.scalProd()是一个先乘后加的方法。对于约束条件而言,不等式约束常常用cplex.addLe()方法。
四、Cplex的使用
4.1 变量定义
● IloNumVar(IloIntVar) :模型需要求解的决策变量。
eg:创建三个连续(离散)变量,lb和ub是变量取值的上限和下限。
IloNumVar[] x = cplex.numVarArray(3, lb, ub);
● IloNumExpr(IloIntExpr):模型的中间变量。通常是关于决策变量的表达式。
eg: 对于x0 + 2x1+3x2,cplex中写为:
IloNumExpr expr = cplex.sum(x[0], cplex.prod(2.0, x[1]),cplex.prod(3.0, x[2]))。
● IloRange : 模型的变量的约束范围。cplex.addLe()和cplex.addEq()的返回值.通常在添加约束的时候用到。
eg: 形如 lb <= expr <= ub,cplex.addLe(10, x1)表示的就是x1>=10。cplex.addEq(double arg0,IloNumExpr arg1): 返回值为IloRange,即arg0<=arg1<=arg0。也
就是arg1的值取值为arg0,相当于赋了一个常数。
● IloObjective :模型的优化目标。cplex.addMaximize()和cplex.addMinimize()的返回值。
eg : 最小化(最大化)目标函数。对于求表达式x1 + 2x2 + 3x3的最小值,cplex中写为(一种写法):
cplex.addMinimize(cplex.sum(x1,cplex.prob(2,x2),cplex.prob(3,x3))).另一种写法:double[] objvals = {1.0, 2.0, 3.0};
cplex.addMinimize(cplex.scalProd(x, objvals))。
4.2 常用方法
● 加法:cplex.sum();
● 减法:cplex.diff();
● 乘法:cplex.prod();
● 除法:cplex.prod();计算除法的方法和乘法的一致,也就是将除法转化为乘法。比如C=A/B就可以转化为A=B*C.
● 累加求和:cplex.scalProd(INumExpr[], double[]);有关括号里的参数类型需要根据具体问题具体分析。
eg:对于x1+2x2+3x3+4x4,cplex中写为:
double[] objvals = {1.0, 2.0, 3.0,4.0};
IloIntVar[]x=cplex.intVarArray(4,lb1,ub1);
cplex.scalProd(x, objvals);
● 平方cplex.square();
● 创建一个0-1变量:cplex.boolVar();
eg:若想定义一个取值为0或1的变量,cplex写为:IloIntVar y=cplex.boolVar();此时y的取值只能是0或1。
● 创建一个数组:cplex.numVarArray(),类似的还有,cplex.intVarArray(),cplex.boolVarArray,cplex.numExprArray()等等;
eg:创建一个包含8个变量的数组,上限为lb,下限为ub、cplex中写为:
IloNumVar[] x = cplex.numVarArray(8, lb, ub);
4.3约束方程的表达
● 不等式约束cplex.addLe();
eg: 对于不等式约束-x[0] + x[1] + x[2] <= 20.0,cplex中写为:
cplex.addLe(cplex.sum(cplex.negative(x[0]), x[1], x[2]), 20);
● 等式约束 cplex.addEq();
eg: 对于等式约束x0==x1,则可以用cplex.addEq(x0,x1)。
● cplex能接受的非线性表达式
1、 min和minl:多个数字表达式的最小值;
2、 max和maxl:多个数字表达式的最大值;
3、 abs:数字表达式的绝对值;
● 线性表达式:cplex.linearNumExpr();
eg: 创建一个线性表达式,求出一数组所有元素的总和。cplex写为:IloNumVar [][] x=new IloNumVar [][] x=new IloNumVar[20][30];
for(int i=0; i < 10; i++) {
IloLinearNumExpr summer=cplex.linearNumExpr();//默认为null.
for(int j=0; j < 10; j++) {
summer.addTerm(1.0, x[i][j]);
}
IloRange con = cplex.addEq(summer, 1);
con.setName("yourConstraintName(" + i + ")");
}
4.4数字表达式的分段线性组合
分段线性函数的表达:cplex.pieceiwiseLinear()
eg:表示一个分段线性函数:当x<=100,斜率为1;100<=x<=200,斜率为2;x>200,斜率为-3。代码实现:
IloNumVar x=cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE);//定义一个实数范围内的x
double [] points={100,200};//两个分割点
double []slopes={1,2,-3};//斜率
IloNumExpr fx=cplex.piecewiseLinear(x1, points, slopes, 0, 300);
4.5求解模型
● IloCplex.solve():精确求解
● IloCplex.solveRelaxed():松弛求解
● IloCplex.getStatus():针对于模型无界或者不可行的情况,可调用该方法得到模型解的状态。
4.6输出格式
● cplex.output().println():cplex中的打印格式。
eg:
cplex.output().println("Solution status = " + cplex.getStatus());
cplex.output().println("Solution value = " + cplex.getObjValue());
● cplex.getObjValue(): 得到目标函数的最值。
● cplex.getValue(): 得到最优目标函数值所对应的最优解x,即决策变量的值。
● cplex.getstatus(): 返回optimal或者其他情况。
4.7 常见的异常
● java.lang.NullPointerException:空指针异常
空指针就是空引用,java空指针异常就是引用本身为空,却调用了方法,这个时候就会出现空指针异常。因为我们在定义一个中间变量IloNumExpr的时候,我们没有对其初始化,又
因该表达式默认是为null,所以,我们有必要为此类变量指定其存储数据的上下限。否则就会产生空指针异常。通常的做法是创建两个变量,一个作为下限,一个作为上限。指定上
限和下限的值,并令上文提到的变量处于上限和下限之间即可。举个例子,以下代码定义了EES_t数组变量的上下限变量lEES_t,uEES_t。然后指定其值分别是0,1000。最后通过
cplex.numVarArray()方法使得数组变量EES_t存储数据的取值范围处于0~1000。
int R1,R2=10;
double[][] lEES_t = new double[R1][R2];
double[][] uEES_t = new double[R1][R2];
for (int i = 0; i < R1; i++) {
for (int j = 0; j < R2; j++) {
lEES_t[i][j] = 0;
uEES_t[i][j] = 1000;
}
}
for (int i=0; i<R1; i++) {
EES_t[i] = cplex.numVarArray(R2, lEES_t[i], uEES_t[i]);
}
五、小结
对比于lingo,Yalmip,guaobi,Cplex展现出了其独特的优势,比如运行速度快,在求解大规模问题上略胜一筹。它提供了python,java,c++等各大编程软件的API,是一款值得一用
的工具。关于Cplex建模的问题,当我们将实际问题建模,并转化为数学公式后,然后将公式写入Cplex的搭好的框架中,求解就很简单了。对于不同的线性或是非线性规划问题模
型,Cplex会依据目标函数的形式以及约束方程去判断模型是属于哪一种规划问题,这一方面显示出了其独特的优势。但Cplex也并非有足够地智能。当我们发现编译通过了,但是求
解的结果是不可行(不可行指的是不存在满足所有约束、界限和整数性限制的解)或者无界(无界指的是可以使目标函数任意大)的状态时,优化器只会告知不可行,我们往往不知道
不可行性的原因是什么。对于小型的模型而言,通过调参排查问题可以解决,但是对于大型的模型,一步一步排查约束条件,变量的范围是否规范等等就会显得力不从心了。当然,
Cplex仍然是一个很强大的工具,有待开发和利用。
六、相关资源
Cplex的官方链接如下:
https://www.ibm.com/support/knowledgecenter/zh/SSSA5P_12.9.0/ilog.odms.studio.help/Optimization_Studio/topics/COS_home.html。
这里包含更加全面的文档资源,供我们参考学习。另外还有运筹优化技术论坛:
]()https://www.optimize.fun/forum.php?mod=forumdisplay&fid=53。
里面有很多的Cplex环境配置问题以及开发中可能会遇到的技术问题的探讨与解答。覆盖的领域范围也比较广泛,涉及交通物流,电力,供应链等等经典的优化问题。