一、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環境配置問題以及開發中可能會遇到的技術問題的探討與解答。覆蓋的領域范圍也比較廣泛,涉及交通物流,電力,供應鏈等等經典的優化問題。
