如何使用cplexAPI求解二次规划及带二次约束的线性规划
R环境下cplexAPI求解二次规划/二次约束规划实现方案
一、二次规划(QP)实现步骤
二次规划是目标函数含二次项、约束均为线性的优化问题,基于cplexAPI的实现流程如下:
- 导入
cplexAPI包,初始化CPLEX环境指针,创建空的问题实例 - 配置问题属性:设置优化方向(最小/最大化),添加决策变量的线性目标系数、上下界、变量类型
- 调用
setQC函数传入目标函数的二次项系数矩阵(CPLEX要求二次矩阵为半正定/半负定,对应最小化/最大化场景,默认使用上三角矩阵存储减少冗余) - 调用
addRows添加所有线性约束,逻辑与线性规划完全一致 - 调用
solveCplex求解,通过getObjVal、getX等函数提取求解结果 - 求解完成后关闭问题实例、释放CPLEX环境,避免内存泄漏
QP代码示例
以下示例为最小化x1² + x2² + x3²,约束x1+x2+x3=1,x1,x2,x3≥0:
library(cplexAPI) # 初始化环境与问题 env <- openEnv() prob <- createProb(env, "qp_example") # 设置为最小化问题 setObjDir(env, prob, CPX_MIN) # 定义3个连续变量,下界0,上界Inf,线性目标系数都是0(目标全为二次项) nvar <- 3 lb <- rep(0, nvar) ub <- rep(CPX_INFBOUND, nvar) obj_lin <- rep(0, nvar) vtype <- rep(CPX_CONTINUOUS, nvar) addCols(env, prob, nvar, obj_lin, lb, ub, vtype, rep("", nvar)) # 设置目标二次项:CPLEX二次目标格式为0.5*x^T Q x,因此要得到x_i²系数为1,Q矩阵对角线元素设为2 Qmat <- Matrix::Diagonal(nvar, 2) # 传入二次矩阵 setQC(env, prob, Qmat@p, Qmat@i, Qmat@x) # 添加线性约束:x1+x2+x3 = 1 ncon <- 1 rhs <- c(1) sense <- c("E") beg <- c(0) ind <- c(0,1,2) val <- c(1,1,1) addRows(env, prob, ncon, rhs, sense, beg, ind, val, rep("", ncon)) # 求解 solveCplex(env, prob) # 提取结果 obj_val <- getObjVal(env, prob) x_sol <- getX(env, prob, 0, nvar-1) print(paste0("最优目标值:", round(obj_val,4))) print(paste0("最优解:x1=", round(x_sol[1],4), ", x2=", round(x_sol[2],4), ", x3=", round(x_sol[3],4))) # 释放资源 freeProb(env, prob) closeEnv(env)
二、带二次约束的线性规划(QCQP)实现步骤
这类问题目标函数为线性,约束中存在二次项,实现流程和QP基本一致,区别是不需要设置目标二次项,改为通过addQConstr函数添加二次约束:
- 初始化CPLEX环境和问题实例,配置决策变量的线性目标系数、上下界、变量类型
- 先添加所有线性约束,逻辑和线性规划一致
- 调用
addQConstr函数逐条添加二次约束,传入二次项的行索引、列索引、系数,以及约束的一次项系数、右端项、约束方向 - 求解后提取结果,释放资源
QCQP代码示例
以下示例为最小化x1+x2+x3,约束x1² + 2x2² ≤ 1、x1+x2+x3 ≥ 0.5,所有变量≥0:
library(cplexAPI) # 初始化环境与问题 env <- openEnv() prob <- createProb(env, "qcqp_example") setObjDir(env, prob, CPX_MIN) # 定义3个连续变量,线性目标系数都是1,下界0 nvar <- 3 obj_lin <- c(1,1,1) lb <- rep(0, nvar) ub <- rep(CPX_INFBOUND, nvar) vtype <- rep(CPX_CONTINUOUS, nvar) addCols(env, prob, nvar, obj_lin, lb, ub, vtype, rep("", nvar)) # 添加线性约束:x1+x2+x3 ≥ 0.5 ncon_lin <- 1 rhs_lin <- c(0.5) sense_lin <- c("G") beg <- c(0) ind <- c(0,1,2) val <- c(1,1,1) addRows(env, prob, ncon_lin, rhs_lin, sense_lin, beg, ind, val, rep("", ncon_lin)) # 添加二次约束:x1² + 2x2² ≤ 1,对应0.5*x^T Q x ≤ 1,因此Q矩阵(0,0)=2、(1,1)=4 qrow <- c(0,1) qcol <- c(0,1) qval <- c(2,4) # 该二次约束无一次项,线性部分留空 lin_ind <- integer(0) lin_val <- numeric(0) addQConstr(env, prob, length(lin_ind), length(qval), 1, "L", lin_ind, lin_val, qrow, qcol, qval, "quad_constr1") # 求解 solveCplex(env, prob) # 提取结果 obj_val <- getObjVal(env, prob) x_sol <- getX(env, prob, 0, nvar-1) print(paste0("最优目标值:", round(obj_val,4))) print(paste0("最优解:x1=", round(x_sol[1],4), ", x2=", round(x_sol[2],4), ", x3=", round(x_sol[3],4))) # 释放资源 freeProb(env, prob) closeEnv(env)
注意事项
- 所有涉及索引的参数均从0开始计数,和CPLEX的C API逻辑保持一致,不要使用R默认的1索引,否则会出现报错或计算错误
- 二次项存储默认使用上三角矩阵,不要重复传入下三角的对称元素,避免系数计算翻倍
- 求解前可以调用
setIntParm(env, prob, CPX_PARAM_SCRIND, 1)打开求解日志输出,方便排查求解过程中的问题 - 若遇到非凸二次规划/二次约束问题,需要开启CPLEX的非凸求解参数:
setIntParm(env, prob, CPX_PARAM_SOLUTIONTARGET, CPX_SOLUTIONTARGET_FIRSTORDER),否则会报错提示问题非凸
内容的提问来源于stack exchange,提问作者wayne
相关产品推荐
相关产品推荐

