【Clion】C++调用【Gurobi】接口快速上手

Clion配置Gurobi

配置前提:已经在环境变量中配置好GRB_LICENSE_FILEGUROBI_HOME

  1. 更改Clion默认编译器:

    设置->构建、执行、部署->工具链->将编译器改为VS。同时注意右侧各项设置 在这里插入图片描述

  2. 配置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;
}

问题决策变量设置

  1. 声明单个决策变量:GRBVar a;

  2. 声明多个决策变量:建议用vector<GRBVar>更高维度也可以嵌套vector

    typedef 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)));
    
  3. 定义单个决策变量:

    GRBVar addVar(double lb, double ub, double obj, char vtype, std::string vname);

    参数分别为 变量下界、变量上界、变量的目标系数、变量类型和变量名称。变量名称可以不写。

    为了加速求解,最好给变量一个尽可能紧的上下界。GRB_INFINITY表示无限大。

    变量目标系数为0.0

    变量类型有:GRB_BINARY、GRB_INTEGER、GRB_CONTINUOUS

  4. 定义多个决策变量:

    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 iDepotxijdj,jDepot可以写成

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=(xa)2+(yb)2 的约束可以用左右平方的处理方式避免输入根式。但是对于右侧有多个根式相加的情况就不能平方了,此时应当将约束拆分。

  1. 定义一个变量 v a r 1 var1 var1,通过添加二次约束model.addQConstr令其等于根式内部的表达式
  2. 通过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
  3. 再将 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(x2a)2+(y2a)2 +(x1a)2+(y1a)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;	//获取求解时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值