Clion配置Gurobi
配置前提:已经在环境变量中配置好GRB_LICENSE_FILE和GUROBI_HOME
-
更改Clion默认编译器:
设置->构建、执行、部署->工具链->将编译器改为VS。同时注意右侧各项设置
-
配置CMakeList文件
cmake_minimum_required(VERSION 3.28) project(LBBD) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXE_LINKER_FLAGS "-static") add_executable(LBBD main.cpp) set(GUROBI_LIB_DIR "$ENV{GUROBI_HOME}\\lib") set(GUROBI_INCLUDE_DIR "$ENV{GUROBI_HOME}\\include") message(STATUS "GUROBI_LIB_DIR: ${GUROBI_LIB_DIR}") message(STATUS "GUROBI_INCLUDE_DIR: ${GUROBI_INCLUDE_DIR}") link_directories(${GUROBI_LIB_DIR}) include_directories(${GUROBI_INCLUDE_DIR}) target_link_libraries(LBBD "${GUROBI_LIB_DIR}\\gurobi110.lib" "${GUROBI_LIB_DIR}\\gurobi_c++mdd2017.lib")
Gurobi测试
输入Gurobi测试例程
/* Copyright 2024, Gurobi Optimization, LLC */
/* This example formulates and solves the following simple MIP model:
maximize x + y + 2 z
subject to x + 2 y + 3 z <= 4
x + y >= 1
x, y, z binary
*/
#include "gurobi_c++.h"
using namespace std;
int
main(int argc,
char *argv[])
{
try {
// Create an environment
GRBEnv env = GRBEnv(true);
env.set("LogFile", "mip1.log");
env.start();
// Create an empty model
GRBModel model = GRBModel(env);
// Create variables
GRBVar x = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, "x");
GRBVar y = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, "y");
GRBVar z = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, "z");
// Set objective: maximize x + y + 2 z
model.setObjective(x + y + 2 * z, GRB_MAXIMIZE);
// Add constraint: x + 2 y + 3 z <= 4
model.addConstr(x + 2 * y + 3 * z <= 4, "c0");
// Add constraint: x + y >= 1
model.addConstr(x + y >= 1, "c1");
// Optimize model
model.optimize();
cout << x.get(GRB_StringAttr_VarName) << " "
<< x.get(GRB_DoubleAttr_X) << endl;
cout << y.get(GRB_StringAttr_VarName) << " "
<< y.get(GRB_DoubleAttr_X) << endl;
cout << z.get(GRB_StringAttr_VarName) << " "
<< z.get(GRB_DoubleAttr_X) << endl;
cout << "Obj: " << model.get(GRB_DoubleAttr_ObjVal) << endl;
} catch(GRBException e) {
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
} catch(...) {
cout << "Exception during optimization" << endl;
}
return 0;
}
如果可以输出结果不报错则代表配置成功。
固定代码格式
try {
// 创建环境
GRBEnv env = GRBEnv("ENV");
// 创建模型
GRBModel model = GRBModel(env);
// 定义变量、目标函数、约束条件
......
//设置求解参数
......
// 求解模型
model.optimize();
}
catch (GRBException e) {
std::cout << "Error code = " << e.getErrorCode() << std::endl;
std::cout << e.getMessage() << std::endl;
}
catch (...) {
std::cout << "Exception during optimization" << std::endl;
}
问题决策变量设置
-
声明单个决策变量:
GRBVar a;
-
声明多个决策变量:建议用
vector<GRBVar>
更高维度也可以嵌套vectortypedef std::vector<GRBVar> GRBVector; typedef std::vector<GRBVector> GRBMatrix; typedef std::vector<GRBMatrix> GRBCube; GRBVector at(nd + 2); GRBMatrix x(nd + 2, GRBVector(nd + 2)); GRBCube y(nd + 2, GRBMatrix(nd + 2, GRBVector(drone_num)));
-
定义单个决策变量:
GRBVar addVar(double lb, double ub, double obj, char vtype, std::string vname);
参数分别为 变量下界、变量上界、变量的目标系数、变量类型和变量名称。变量名称可以不写。
为了加速求解,最好给变量一个尽可能紧的上下界。GRB_INFINITY表示无限大。
变量目标系数为0.0
变量类型有:GRB_BINARY、GRB_INTEGER、GRB_CONTINUOUS
-
定义多个决策变量:
for (int i = 0; i < nd + 2; i++) { at[i] = model.addVar(0.0, 1.0, 0, GRB_CONTINUOUS, "at" + std::to_string(i)); for (int j = 0; j < nd + 2; j++) x[i][j] = model.addVar(0.0, 1.0, 0, GRB_BINARY, "x" + std::to_string(i) + std::to_string(j)); }
目标函数设置
首先定义一个线性表达式GRBLinExpr obj = 0.0;
,对于求和号内部的表达式用for循环累加。
然后在模型中加入目标函数即可model.setObjective(obj, GRB_MINIMIZE);
如果是最大化应该写GRB_MAXIMIZE
例如
GRBLinExpr obj = 0.0;
for (int i = 0; i < nd + 2; i++) {
obj += (at[i] + wt[i]) * truck_cost;
for (int j = 0; j < nd + 2; j++)
obj += x[i][j] * disd[i][j] * truck_cost / truck_speed;
for (int k = 0; k < drone_num; k++) {
obj += (dis_start[i][k] + dis_retrv[i][k]) * drone_cost1 / drone_speed1;
obj += dis_check[i][k] * drone_cost2 / drone_speed2;
}
}
约束条件设置
约束条件与目标函数写法类似,对于线性约束同样是定义一个GRBLinExpr然后用for循环累加或者一次写入多个约束条件
GRBConstr addConstr(const GRBTempConstr& tc, const char* name=0);
参数分别为约束条件和约束条件名称(可选)
例如对于 ∑ i ∈ D e p o t x i j ≤ d j , ∀ j ∈ D e p o t \sum_{i\in Depot}{x_{ij}}\leq d_j,\quad\forall j\in Depot ∑i∈Depotxij≤dj,∀j∈Depot可以写成
GRBLinExpr c1 = 0.0;
for (int j = 0; j < nd + 2; j++) {
c1 = 0.0;
for (int i = 0; i < nd + 2; i++)
c1 += x[i][j];
model.addConstr(c1 <= d[j], "C5 " + std::to_string(j));
}
注意等式约束要写==
而不是=
Gurobi也能支持一些非线性约束条件。包括 m a x max max、 m i n min min、绝对值、与、或、指示约束、 y = e x y=e^x y=ex等等,见GRBModel::addGenConstrXxx() - Gurobi Optimization。此处着重介绍如何处理根式和分段线性函数约束。
根式约束
对于一般的形如 y = ( x − a ) 2 + ( y − b ) 2 y=\sqrt{(x-a)^2+(y-b)^2} y=(x−a)2+(y−b)2的约束可以用左右平方的处理方式避免输入根式。但是对于右侧有多个根式相加的情况就不能平方了,此时应当将约束拆分。
- 定义一个变量
v
a
r
1
var1
var1,通过添加二次约束
model.addQConstr
令其等于根式内部的表达式 - 通过
model.addGenConstrPow
将 v a r 1 var1 var1转换成 v a r 2 var2 var2,其中 v a r 2 = v a r 1 var2=\sqrt{var1} var2=var1 - 再将 v a r 2 var2 var2作为整体带入初始表达式中计算。
例如约束 y ≤ ( x 2 − a ) 2 + ( y 2 − a ) 2 + ( x 1 − a ) 2 + ( y 1 − a ) 2 y\leq\sqrt{(x_2-a)^2+(y_2-a)^2}+\sqrt{(x_1-a)^2+(y_1-a)^2} y≤(x2−a)2+(y2−a)2+(x1−a)2+(y1−a)2可以表示为
GRBVar pw1 = model.addVar(0, GRB_INFINITY, 0, GRB_CONTINUOUS);
GRBVar pw2 = model.addVar(0, GRB_INFINITY, 0, GRB_CONTINUOUS);
GRBVar sq1 = model.addVar(0, GRB_INFINITY, 0, GRB_CONTINUOUS);
GRBVar sq2 = model.addVar(0, GRB_INFINITY, 0, GRB_CONTINUOUS);
model.addQConstr(pw1 == ((x2 - a) * (x2 - a) + (y2 - a) * (y2 - a)));
model.addQConstr(pw2 == ((x1 - a) * (x1 - a) + (y1 - a) * (y1 - a)));
model.addGenConstrPow(pw1,sq1,0.5);
model.addGenConstrPow(pw2,sq2,0.5);
model.addConstr(y<=sq1+sq2);
其中GRBGenConstr addGenConstrPow(GRBVar xvar, GRBVar yvar, double a, const char* name=0,std::string options="");
表示添加一条形如
y
=
x
a
y=x^a
y=xa的约束
参数分别为
x
x
x,
y
y
y,
a
a
a,约束名称(可选),约束选项(可选)。其中约束选项可以具体设置使用分段线性函数近似此约束的精确度。例如FuncPieces=-1 FuncPieceError=0.001
分段线性约束
Gurobi中可以使用GRBGenConstr addGenConstrPWL(GRBVar xvar, GRBVar yvar, int npts, const double* xpts,const double* ypts, const char* name=0);
函数添加一个形如
y
=
f
(
x
)
y=f(x)
y=f(x)的分段线性约束,其中
f
(
x
)
f(x)
f(x)是分段线性函数。
参数分别为 x x x, y y y,分段函数的分段点个数,分段点横坐标数组,分段点纵坐标数组,约束名称(可选)。如果使用了vector存储x和y的坐标,则可以使用
auto* pxdouble = new double[px.size()];
std::copy(px.begin(),px.end(),pxdouble);
进行类型转换。
模型求解
在求解模型前可以对求解器提出一些要求,即求解参数。
model.set("TimeLimit","7200.0"); //限制求解时间最长为7200秒
model.set("MIPGap","1e-4"); //要求上下界差值为0.01%时停止求解
model.set("FuncNonLinear","1"); //要求切换求解算法,使得求解混合整数非线性模型更快、精度更高
model.set("DisplayInterval","10"); //设置输出间隔时长为10秒
model.set("LogFile",logname); //设置输出命名为logname的求解日志文件
全部参数见Parameter Descriptions - Gurobi Optimization
设置好参数后model.optimize()
即可开始求解。
模型信息
求解结束后可以使用model.get()
函数获取模型求解的结果(求解时间,变量取值,目标函数等)
std::cout << x.get(GRB_StringAttr_VarName) << " "<< x.get(GRB_DoubleAttr_X) << std::endl;
std::cout << y.get(GRB_StringAttr_VarName) << " "<< y.get(GRB_DoubleAttr_X) << std::endl;
std::cout << z.get(GRB_StringAttr_VarName) << " "<< z.get(GRB_DoubleAttr_X) << std::endl;
/*获取决策变量取值*/
std::cout << "Obj: " << model.get(GRB_DoubleAttr_ObjVal) << std::endl; //获取目标函数
std::cout << "Time:" << model.get(GRB_DoubleAttr_Runtime) <<std::endl; //获取求解时间