一、Cplex简介
Cplex是IBM公司的一个优化问题求解器。主要用于求解线性规划,混合整数规划、二次规划等问题。
Cplex求解速度快,使用简单易上手。除了自带的语言外,cplex可以利用C++、Java、Python等语言使用。对于运筹优化方向的问题求解事半功倍。
二、Cplex下载与安装
Cplex可以从官方网站利用电子邮件注册下载,网址如下:
https://www.ibm.com/analytics/cplex-optimizer 社区版的可以随便下载,但是变量个数有限制。
安装完成后可以查看版本,这个是社区版。
三、设置环境变量
1.IBM cplex的官方文档《Getting Started with CPLEX》
网址如下 https://www.ibm.com/support/knowledgecenter/SSSA5P_12.7.1/ilog.odms.studio.help/pdf/gscplex.pdf
初次使用 Cplex 的基础文档,里面包含了如何安装使用Cplex及在不同语言环境下的配置方法。
2.设置环境变量
通过对官方文档的查看,在windows中需要配置环境变量。打开控制面板,win10中可以在设置中搜索打开。总体步骤如下:
控制面板—系统—高级—环境变量—系统变量中查看Path—添加图上路径
笔者的Cplex装在D盘中,所以路径示例如上。
四、配置VS C++ 2019调用 CPLEX 接口
笔者将大部分时间都耗费在了对VS C++的环境配置中,因为采坑不少,所以在此将过程总结出来,希望可以帮助大家减少试错时间。
1.将调试环境修改为 Release x64
将VS调试环境修改为release, x64. 如图所示。(代码是官方示例,附在文末)
笔者之前没有注意修改调试环境,次次出错。VS调试环境默认是Debug模式。Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。x86平台编译出来的可执行文件都是32位的。x64对应的则是64位的。Cplex是64位的,所以VS中的环境配置也必须是64位2.将解决方案属性也修改为 Release x64
在解决方案管理器中右键点击测试文件Cplex的属性,配置为release ,x64。
3.添加include的路径
在c++中调用cplex,需要让vs知道include的各种路径。
具体的办法是:
属性页面——附加包含目录——编辑——点击带星号的文件夹——增加路径
具体的路径需要根据自己电脑上的cplex安装位置匹配。其实就是两个文件夹路径:
cplex\concert\include
cplex\include
4.链接库
这一步的目的是让 C++ 的 Linker(链接器) 知道cplex以及 Concert 的库在哪里,以便调用。如下图所示:
5.添加附加依赖项
将具体的lib链接进去。
D:\Cplex\concert\lib\x64_windows_msvc14\stat_mda\concert.lib
D:\Cplex\cplex\lib\x64_windows_msvc14\stat_mda\cplex12100.lib
D:\Cplex\cplex\lib\x64_windows_msvc14\stat_mda\ilocplex.lib
具体的路径要根据自己文件所在的位置!!!不要直接copy,不然连接器还是找不到6.添加预处理器指令
注意不要敲错,添加之后应用。7.注意调整代码生成的运行库为 多线程DLL(/MD)
一般是默认的,但是笔者踩坑时就是有过因为代码生成的运行库没有调整,因此也列出来以便查阅。
以上七步结束后,就可以在vs c++ 2019中调用cplex啦~
可以用官方提供的测试代码试试:
#include <ilcplex/ilocplex.h>
ILOSTLBEGIN
int
main(int argc, char** argv)
{
IloEnv env;
try {
IloModel model(env);
IloNumVarArray vars(env);
vars.add(IloNumVar(env, 0.0, 40.0));
vars.add(IloNumVar(env));
vars.add(IloNumVar(env));
model.add(IloMaximize(env, vars[0] + 2 * vars[1] + 3 * vars[2]));
model.add(-vars[0] + vars[1] + vars[2] <= 20);
model.add(vars[0] - 3 * vars[1] + vars[2] <= 30);
IloCplex cplex(model);
if (!cplex.solve()) {
env.error() << "Failed to optimize LP." << endl;
throw(-1);
}
IloNumArray vals(env);
env.out() << "Solution status = " << cplex.getStatus() << endl;
env.out() << "Solution value = " << cplex.getObjValue() << endl;
cplex.getValues(vals, vars);
env.out() << "Values = " << vals << endl;
}
catch (IloException& e) {
cerr << "Concert exception caught: " << e << endl;
}
catch (...) {
cerr << "Unknown exception caught" << endl;
}
env.end();
return 0;
}
五、在python中调用Cplex
在python中调用Cplex的接口相对比较简单,官方文档里给了两条路径。
但是相对来说还是比较繁琐,其实本身就是调用cplex的库嘛,直接用pip安装第三方库 cplex就可以了。如果用anaconda的话可以直接在anaconda prompt中安装
But,笔者在多次安装后都提示失败,没有找到相应的版本,无奈准备用官方的方法时,发现cplex文件夹里只支持python3.6 和3.7 的版本。
而笔者现在用的python和anaconda的python版本都是3.8,所以屡次报错。当然也不需要重新安装了,回滚到python3.7版本就好啦~以anaconda 为例,利用python -V命令查看目前python版本:
笔者这里已经回滚到3.7了,如果是3.8及以上,可以利用
conda install python=3.7
**将anaconda的python版本回滚到3.7。**然后就可以利用pip命令在anaconda中安装cplex库啦~注意新版的anaconda里面是搜不到cplex库的,因为目前cplex 12.10.0.0最高只能支持到python3.7。所以相对而言这是一个比较简便的方法。
安装完cplex包之后环境就配置好啦~可以利用官方提供的代码示例试试!
```python
execfile("cplexpypath.py")
import cplex
from cplex.exceptions import CplexError
import sys
# data common to all populateby functions
my_obj = [1.0, 2.0, 3.0]
my_ub = [40.0, cplex.infinity, cplex.infinity]
my_colnames = ["x1", "x2", "x3"]
my_rhs = [20.0, 30.0]
my_rownames = ["c1", "c2"]
my_sense = "LL"
def populatebyrow(prob):
prob.objective.set_sense(prob.objective.sense.maximize)
# since lower bounds are all 0.0 (the default), lb is omitted here
prob.variables.add(obj = my_obj, ub = my_ub, names = my_colnames)
# can query variables like the following bounds and names:
# lbs is a list of all the lower bounds
lbs = prob.variables.get_lower_bounds()
# ub1 is just the first lower bound
ub1 = prob.variables.get_upper_bounds(0)
# names is ["x1", "x3"]
names = prob.variables.get_names([0, 2])
rows = [[[0,"x2","x3"],[-1.0, 1.0,1.0]],
[["x1",1,2],[ 1.0,-3.0,1.0]]]
prob.linear_constraints.add(lin_expr = rows, senses = my_sense,
rhs = my_rhs, names = my_rownames)
# because there are two arguments, they are taken to specify a range
# thus, cols is the entire constraint matrix as a list of column vectors
cols = prob.variables.get_cols("x1", "x3")
def populatebycolumn(prob):
prob.objective.set_sense(prob.objective.sense.maximize)
prob.linear_constraints.add(rhs = my_rhs, senses = my_sense,
names = my_rownames)
c = [[[0,1],[-1.0, 1.0]],
[["c1",1],[ 1.0,-3.0]],
[[0,"c2"],[ 1.0, 1.0]]]
prob.variables.add(obj = my_obj, ub = my_ub, names = my_colnames,
columns = c)
def populatebynonzero(prob):
prob.objective.set_sense(prob.objective.sense.maximize)
prob.linear_constraints.add(rhs = my_rhs, senses = my_sense,
names = my_rownames)
prob.variables.add(obj = my_obj, ub = my_ub, names = my_colnames)
rows = [0,0,0,1,1,1]
cols = [0,1,2,0,1,2]
vals = [-1.0,1.0,1.0,1.0,-3.0,1.0]
prob.linear_constraints.set_coefficients(zip(rows, cols, vals))
# can also change one coefficient at a time
# prob.linear_constraints.set_coefficients(1,1,-3.0)
# or pass in a list of triples
# prob.linear_constraints.set_coefficients([(0,1,1.0), (1,1,-3.0)])
def lpex1(pop_method):
try:
my_prob = cplex.Cplex()
if pop_method == "r":
handle = populatebyrow(my_prob)
if pop_method == "c":
handle = populatebycolumn(my_prob)
if pop_method == "n":
handle = populatebynonzero(my_prob)
my_prob.solve()
except CplexError, exc:
print exc
return
numrows = my_prob.linear_constraints.get_num()
numcols = my_prob.variables.get_num()
print
# solution.get_status() returns an integer code
print "Solution status = " , my_prob.solution.get_status(), ":",
# the following line prints the corresponding string
print my_prob.solution.status[my_prob.solution.get_status()]
print "Solution value = ", my_prob.solution.get_objective_value()
slack = my_prob.solution.get_linear_slacks()
pi = my_prob.solution.get_dual_values()
x = my_prob.solution.get_values()
dj = my_prob.solution.get_reduced_costs()
for i in range(numrows):
print "Row %d: Slack = %10f Pi = %10f" % (i, slack[i], pi[i])
for j in range(numcols):
print "Column %d: Value = %10f Reduced cost = %10f" % (j, x[j], dj[j])
my_prob.write("lpex1.lp")
if __name__ == "__main__":
if len(sys.argv) != 2 or sys.argv[1] not in ["-r", "-c", "-n"]:
print "Usage: lpex1.py -X"
print " where X is one of the following options:"
print " r generate problem by row"
print " c generate problem by column"
print " n generate problem by nonzero"
print " Exiting..."
sys.exit(-1)
lpex1(sys.argv[1][1])
else:
prompt = """Enter the letter indicating how the problem data should be populated:
r : populate by rows
c : populate by columns
n : populate by nonzeros\n ? > """
r = ’r’
c = ’c’
n = ’n’
lpex1(input(prompt))