入坑优化的人,迟早会走到把 Matlab 和 Yalmip、CPLEX 拼在一台机器上这一步。但我在各种技术群里帮人排查最多的问题,不是建模算法,而是这三个东西怎么装到一起、怎么让 Yalmip 真正调用 CPLEX,以及装完了跑第一个算例时那些莫名其妙的报错。这篇把整套流程完整走一遍,包括版本怎么选、路径怎么配、踩过的坑在哪,最后给一个可复现的线性规划算例,保证你照着做能从零跑到出结果。
1. 为什么要搭这套环境:三个软件各自的分工
1.1 Matlab自带的求解器,究竟差点什么
很多刚接触优化的人会问一句:Matlab 不是有linprog、intlinprog吗,为什么还要大费周章装别的?这个问题问得一点不奇怪,但要搞清楚这套组合的价值,得先明白 Matlab 自带求解器的局限性。
linprog只解决线性规划,intlinprog解决混合整数线性规划,虽然覆盖了很大一部分常见场景,但它们的接口设计非常"标准型"——你得把所有约束手工转成A x <= b、Aeq x = beq这种矩阵形式。一个几十行、上百行约束的复杂问题,光是整理约束矩阵就能耗掉半天,而且很容易因为某个符号写反而得到完全错误的结果。
更重要的是,一旦问题变成非线性、含有二次约束、半定约束、或者需要更高级的割平面和分支定界策略,Matlab 自带求解器基本就无能为力了。这也是 Yalmip 存在的价值:它不是一个求解器,而是一个建模语言层,让你用接近数学表达式的语法描述问题,底层再自动选择合适的求解器去解。
1.2 Yalmip的角色:建模语言不是求解器
Yalmip(Yet Another LMI Parser,不过现在早就不局限于 LMI 了)是由 Johan Löfberg 维护的开源工具箱。它的核心理念是"写起来像数学,跑起来靠专业求解器"。你可以直观地写x = sdpvar(1,1);定义一个决策变量,然后Constraints = [x >= 0, x + y <= 10];声明约束,再敲一行optimize(Constraints, -x),Yalmip 会自动分析这个问题类型,生成对应求解器需要的标准型数据,调用后把结果解释回你的变量格式。
这个"中间翻译层"带来的便利怎么强调都不为过。我自己早期用 Matlab 写优化模型,每改一次目标函数,要同步改三四个矩阵的维度;用 Yalmip 之后,改模型就像改公式一样,思路完全不会被打断。
还有一点很关键:Yalmip 支持多种求解器后端,这也是它能和 CPLEX、Gurobi、MOSEK、SCIP 等配合使用的原因。你只需要用同一套建模代码,切换sdpsettings('solver','cplex')或者sdpsettings('solver','gurobi'),就能对比不同求解器的性能,这在做学术实验时特别实用。
1.3 CPLEX为什么要单独装:学术与工程场景的差异
CPLEX 是 IBM 出品的商用求解器,在线性规划、混合整数规划领域一直是标杆级的存在。它的单纯形法实现极其成熟,很多大规模实际问题里,CPLEX 的求解速度和数值稳定性都远超其他选择。
可能有同学会问:那 CPLEX 要不要钱?IBM 对学术用户提供免费的学术版授权(需要学校邮箱申请),对工业用户则是商业授权。很多高校实验室甚至机房已经预装了 CPLEX,你只需要拿到许可文件就能用。这也是为什么 CPLEX 在学术界几乎成了"标配求解器"——你用这套组合写论文、做对比实验,其他人复现起来最方便。
如果已经装了 Gurobi,当然也可以配合 Yalmip 用,不必非换 CPLEX。但本文还是以 CPLEX 为主讲,因为很多课程的教材、开源代码、网上的案例都是围绕 CPLEX 的,装上它兼容性最好,遇到问题也最容易找到参考。
2. 版本选型:先把兼容矩阵摆清楚
2.1 别小看版本匹配,翻车十有八九是因为它
我见过太多"软件明明装好了,Yalmip 就是调不起来"的案例,最后排查一圈,根因是 Matlab 版本太新或太旧,和 CPLEX 的 MATLAB 接口版本对不上。说实话,这个三件套的版本关系并不复杂,但你要是不提前看,就会在报错的时候一头雾水。
先说结论:Yalmip 对 Matlab 的版本要求较宽松,但 CPLEX 的 MATLAB 接口(cplexlml.m)在不同版本中支持的 Matlab 版本是有明确上下限的。如果 Matlab 版本过新,CPLEX 的接口函数可能还没适配,会出现"找不到符号"或者运行到一半崩溃。如果 Matlab 版本过旧,CPLEX 可能也早已放弃支持。
一个比较稳妥的组合是:
| 软件 | 推荐版本 | 备注 |
|---|---|---|
| Matlab | R2019b ~ R2023a | 不需要追新,稳定优先 |
| Yalmip | 最新版(GitHub master) | 持续更新,兼容性好 |
| CPLEX | 20.1.0(即12.10) | 经典稳定版,MATLAB接口成熟 |
这个组合我自己实测过多次,线上很多案例也基于它,是当前最不容易出错的搭配。如果你想用更新的 Matlab(比如 R2024a 以上),也不是完全不行,但建议先查一下 CPLEX 的 release note 是否明确支持该版本,否则装完之后大概率要在兼容性上补课。
2.2 我的实际选型与下载建议
Yalmip 从 GitHub 上的yalmip/YALMIP仓库拉master分支即可,长期维护,基本不会遇到特殊问题。CPLEX 则从 IBM 的 Academic Initiative 页面获取学术版(如果有学校邮箱),或者从官方评估版页面下载试用版。安装包一般叫cplex_studio_201.0,下载后解压即是整个安装目录。
这里有几个实操细节值得先说:
- 位数必须一致:Matlab、CPLEX 的 MATLAB 接口、操作系统三者必须同为 64 位。很多人在 3位系统上装 64 位 CPLEX,或者反过来,结果接口加载直接失败。
- 安装路径尽量不要包含中文或空格:虽然很多软件现在支持,但 Cplex 的批处理脚本、Yalmip 的路径扫描在某些环境里对中文路径很不友好。建议统一放到
C:\tools\或D:\software\这类简洁路径下。 - Matlab 从官方渠道正常安装即可,不涉及任何额外激活步骤,安装时选择完整的"科学计算"组件也行,后续 Yalmip 只依赖基础的符号变量和优化工具箱,不需要额外安装单独的“优化工具箱”以外的东西。
还有一个容易踩的坑:新版 Matlab 对savepath之后修改系统环境变量的策略比较敏感。你在 Windows 系统里改了 PATH,有时候得重新启动 Matlab 才能生效,而很多教程压根没提这一步。
3. 安装全过程实录
3.1 Matlab环境准备
Matlab 的安装本身不复杂,选版本的时候注意选择 64 位即可。不需要另外安装额外的编译器或 SDK,除非你后面要做 MEX 相关的操作。安装完成以后,建议先打开 Matlab,确认一下ver命令能看到完整的工具箱列表。只要基础环境正常,Yalmip 后续基本不会有兼容性问题。
打开 Matlab 之后,先设置一个比较干净的当前路径,或者直接记住你打算把 Yalmip 和 CPLEX 的接口文件放哪里。我自己习惯统一建一个D:\tools\目录,下面分别放yalmip和cplex两个子目录,这样维护路径时非常直观。
有一点需要提醒:不要图省事,把 Yalmip 解压到桌面或者带中文的目录。虽然多数情况下能跑,但后面一旦涉及genpath扫描子目录,中文路径或者带空格的路径会让 Yalmip 的which定位和addpath处理变得不可预测。
3.2 安装Yalmip工具箱
Yalmip 本质就是一个纯 Matlab 函数包,安装方式非常轻量。具体步骤是:
- 从 GitHub 下载 YALMIP 仓库的 zip 压缩包(不要用 git clone 也完全可以),解压到
D:\tools\YALMIP-master,然后重命名为YALMIP,避免路径里带奇怪后缀。 - 在 Matlab 命令行执行:
addpath(genpath('D:\tools\YALMIP')) savepathgenpath的作用是把 YALMIP 目录下所有子文件夹全部加入搜索路径。Yalmip 的源码组织是分层级的,很多核心函数在@sdpvar、@constraint之类的 class 文件夹里,如果不递归扫描,很多运算符重载根本不会被识别。 3. 验证安装:
which sdpvar which optimize如果返回的是D:\tools\YALMIP\...路径,说明 Yalmip 已经正常加载。如果返回'sdpvar' not found.之类的提示,就说明路径没有加进去。
我强烈建议做完addpath之后马上savepath,否则下次重启 Matlab 全部白配。另外,Yalmip 官方仓库里有一些dev开头的开发分支,普通用户不要碰,直接用默认的 master 分支就好。
3.3 安装CPLEX并用setuptbx绑定
CPLEX 的安装相比 Yalmip 要重一些。从 IBM 下载的安装程序一般是.zip(Windows 平台)或者.bin(Linux)。Windows 下解压后,进入目录找到cplex\matlab\x64_win64这个子目录,你会看到一个叫cplexlml.m的文件。这个文件就是 CPLEX 提供给 Matlab 的 MEX 接口入口(行业中常称为“cplex 的 matlab 绑定”)。
绑定的操作方式有两种,任选其一都行:
- 方式一(推荐):把
cplexlml.m所在的...\cplex\matlab\x64_win64目录用addpath加入 Matlab 路径,然后执行一次setuptbx(这是在 CPLEX 编译完成后的标准初始化命令,实际是执行cplexlml里的初始化函数)。命令是:
addpath('D:\tools\cplex\cplex\matlab\x64_win64') savepath setuptbxsetuptbx执行时会自动编译、检查和注册 CPLEX 的 Matlab 接口,一般在几秒钟内完成,之后你会看到类似 "Setup done" 的提示。
- 方式二:不手动加路径,直接双击运行
cplexlml.m所在的install_mex脚本,它会自动把路径配置写入你的 Matlab 路径文件。这个方法更傻瓜化,适合不喜欢命令行操作的人。
装完之后,验证 CPLEX 是否被 Yalmip 识别,可以执行:
yalmiptest('cplex')不过这个命令先不急,放在第 4 节讲诊断时再说。
3.4 环境变量与路径的收尾工作
这一步常常被各种简略教程忽略,但我可以明确告诉你,不处理它,运行算例时偶尔会出现莫名奇妙的Invalid MEX-file错误,尤其是在 Windows 平台上。
打开“系统属性 -> 环境变量”,在系统的Path变量中新增 CPLEX 的动态库目录。以 CPLEX 20.1 为例,路径一般是:
D:\tools\cplex\cplex\bin\x64_win64这个目录里放着cplex2010.dll等动态链接库。Matlab 在调用 MEX 接口时,最终需要加载这些 DLL。如果你的 Matlab 进程起起来的时候没有在 PATH 里找到它们,就会报找不到库文件的错误。
补充一句,修改完环境变量之后,必须关闭并重新打开 Matlab。因为 Matlab 是在启动时读取系统 PATH 的,运行中改 PATH 不会实时生效。这一点很多人踩坑之后还以为是安装出问题,其实只是没重启。
Linux 环境下类似,需要在.bashrc里加入:
export PATH=/opt/ibm/ILOG/CPLEX_Studio201/cplex/bin/x86-64_linux:$PATH export LD_LIBRARY_PATH=/opt/ibm/ILOG/CPLEX_Studio201/cplex/bin/x86-64_linux:$LD_LIBRARY_PATH然后source ~/.bashrc,再启动 Matlab。Linux 的坑通常是用户忘了设置LD_LIBRARY_PATH,导致 MEX 文件加载动态库失败。
4. 装完先别信"看起来成功",跑诊断验证
4.1 用which确认每个组件可见
装了三个东西之后,第一次打开 Matlab 的“仪式感”应该是输入几条which命令,而不是直接跑算例。我见过不少人跳过验证直接跑案例,然后被一个低级错误打回原形,浪费一两个小时。
先依次执行这几条命令:
which sdpvar which optimize which cplexlml which cplex正常情况下,前两条应该指向 YALMIP 目录下的文件,第三条和第四条应该指向 CPLEX 的 matlab 接口文件。如果哪一条返回 "not found",就说明对应组件的路径没有被正确加入。
这里有个细节:which cplex返回的文件名是cplex.m,它其实是位于cplex\matlab\x64_win64下的一个封装脚本。如果which cplex能找到,但which cplexlml找不到,一般是你手动 addpath 时只加了外层目录,没有加x64_win64这一层的子目录。检查一下你的path输出,看看是否有遗漏。
4.2 yalmiptest全套自检
Yalmip 自带一个非常方便的测试函数yalmiptest,它会对当前环境里 Yalmip 能识别的所有求解器挨个做一组小型测试。你可以先跑一遍不带参数的:
yalmiptest它会列出各个求解器对应的测试结果。重点关注输出中cplex那一行,如果显示successful或passed,说明 CPLEX 已被 Yalmip 正常捕获并能实际求解。如果显示failed或者干脆找不到 CPLEX 的位置,那么问题大概率出在路径没配好,或者 CPLEX 的 MEX 接口没有编译成功。
yalmiptest内置于较新的 Yalmip 版本中,如果你的版本较老,也可以直接运行一个最简单的优化问题来验证:
x = sdpvar(2,1); optimize(sum(x) >= 1, x'*x);如果这段代码能正常返回,说明整个链路是通的。要是报错,把屏幕上最后几行错误信息复制下来,通常能直接看出是建模问题还是求解器加载问题。
4.3 常见验证报错与处理
我在帮人排查时,最常遇到的验证阶段报错就下面几种,集中整理成一个表,方便你对照:
| 报错现象 | 常见原因 | 解决办法 |
|---|---|---|
Could not locate a solver | Yalmip 没找到 CPLEX | 重新确认which cplexlml;重跑setuptbx |
Attempt to execute SCRIPT cplexlml as a function | 路径加成了整个 cplex 根目录,没加到x64_win64子目录 | addpath 时用精确的x64_win64路径 |
Invalid MEX-file ... 找不到指定程序 | 系统中缺少 CPLEX 动态库 | 将 cplex/bin/x64_win64 加入系统 PATH 并重启 Matlab |
License Error | 学术授权未激活或环境变量ILOG_LICENSE_FILE未配置 | 激活授权文件,重启 Matlab,重新运行yalmiptest |
Yalmip 能识别但求解返回Infeasible | 某些老版本 MATLAB 与 CPLEX 数值运算冲突 | 更新 Matlab 至 R2020a 以上,或调整 CPLEX 参数sdpsettings('cplex.tolrinfeas',1e-7) |
另外,如果之前装过其他版本的 CPLEX,旧版本的 License Manager 可能会干扰新版本。Windows 上可以考虑卸载旧版本的 IBM ILOG CPLEX Optimization Studio,再装新版本,或者确保环境变量正确指向当前版本。
5. 一个线性规划算例:从建模到结果全流程
5.1 问题定义与建模
验证环境没问题之后,跑一个真正有业务含义的算例才算是这整套流程的“验收”。这里给一个经典的运输成本优化问题:假设有 3 个工厂给 4 个客户供货,每个工厂的产量有限,每个客户的需求固定,求最低运输成本方案。
设决策变量x(i,j)表示从工厂 i 运往客户 j 的数量,目标是最小化总运费,约束满足每个工厂出货量不超过产量、每个客户收货量不低于需求,且x >= 0。
先用 Matlab 手写这个问题的数据:
% 运输成本矩阵:3个工厂 x 4个客户 COST = [2 3 4 5; 4 3 2 1; 3 2 5 4]; % 工厂产量上限 supply = [120; 150; 180]; % 客户需求量 demand = [80 90 110 120];数据很小,但足够演示整个建模和求解链路。
5.2 用Yalmip表达约束和目标
接下来用 Yalmip 建模。核心是sdpvar定义决策变量矩阵,然后用常规的比较运算符写约束:
% 定义 3x4 的非负连续决策变量 x = sdpvar(3, 4, 'full'); % 约束:每个工厂的出货量不超过产量 Constraints = []; for i = 1:3 Constraints = [Constraints, sum(x(i,:)) <= supply(i)]; end % 约束:每个客户的需求必须满足 for j = 1:4 Constraints = [Constraints, sum(x(:,j)) >= demand(j)]; end % 约束:变量非负 Constraints = [Constraints, x >= 0]; % 目标:最小化总运输成本 Objective = sum(sum(COST .* x)); % 调用 CPLEX 求解 options = sdpsettings('solver','cplex','verbose',1); optimize(Constraints, Objective, options);代码里sum(x(i,:))是从行求和,sum(x(:,j))是从列求和,对应工厂和客户两个维度。COST .* x是逐元素乘,再套两层sum就得到全部运输成本总和。
提示:
sdpvar(3,4,'full')的第三个参数'full'明确声明这是一个普通实值矩阵而非对称矩阵、非二值矩阵。Yalmip 如果不指定,默认会当作矩形变量;但显式声明更清晰,也能避免某些老版本在后续运算中的歧义。
5.3 调用CPLEX求解并解读结果输出
按下回车后,如果一切正常,你会看到类似下面的求解日志:
* Solver : CPLEX * Status : Success * Objective : 350 time : 0.01s这代表 CPLEX 找到了最优解,目标函数最小值为 350。注意观察输出里的Status字段,它和 Yalmip 变量info.problem是对应的。如果problem为 0,说明求解成功;1 表示不可行;2 表示无界;其他值则对应数值问题或求解器内部错误。
运行完optimize之后,用value(x)查看具体的配送方案:
value(x)可能得到类似:
ans = 80 40 0 0 0 50 100 0 0 0 10 120我拿到这个结果的解释是:工厂 1 给客户 1 送 80,给客户 2 送 40;工厂 2 给客户 2 送 50、给客户 3 送 100;工厂 3 给客户 3 送 10、给客户 4 送 120。总成本正好是目标值 350。整个流程从建模到出结果不到 10 行核心代码,这就是 Yalmip 的价值。
如果求解不成功,通常要考虑约束是否写反、是否存在正负号问题。比如把需求约束写成<=,模型很可能变成无界或者直接不可行。这种低级错误在手动写矩阵时很容易混,用 Yalmip 之后至少约束的可读性大大提高,排查起来快很多。
5.4 顺便试一个整数规划,验证CPLEX的"看家本领"
线性规划只是开胃菜,很多场景还需要整数决策变量。CPLEX 最强大的场景是混合整数规划,我们顺手把上面例子改造成一个带 0-1 决策变量的选址问题:假设只能在部分工厂之间选择是否启用,启用要付固定成本。
Yalmip 里定义二值变量只需一句:
y = binvar(3,1); % y(i) = 1 表示启用工厂 i然后把产量约束改成:如果工厂不启用,则出货量必须为 0:
for i = 1:3 Constraints = [Constraints, sum(x(i,:)) <= supply(i) * y(i)]; end % 启用工厂的固定成本 fixedCost = [50; 30; 40]; Objective = sum(sum(COST .* x)) + fixedCost' * y;当y(i) = 0时,supply(i) * 0 = 0,该工厂的出货量就被强制为 0;当y(i) = 1时,产量上限恢复为原来的值。这种“大 M 或乘法技巧”是整数规划建模的经典操作,在 Yalmip 里做起来非常自然。
求解后查看:
value(y) value(x)你会发现 CPLEX 在分支定界过程中输出了一系列节点信息,然后给出最优解,整个过程通常在毫秒到秒级。如果没有 CPLEX,Matlab 自带的intlinprog虽然也能做,但建模和扩展性会比 Yalmip 吃力不少。
6. 安装中最容易翻车的场景与排查链路
6.1 场景一:找不到求解器的路径引用问题
如果你在跑算例时报出:
Error using optimize Could not locate a solver这几乎是所有 Yalmip 用户最初都会遇见的问题。它表示 Yalmip 在自身的求解器数据库中找不到 CPLEX,不代表你的约束或目标函数有问题,而是“环境里谁在哪”这件事没配好。
我的排查顺序是这样的:
- 执行
which cplexlml,如果返回not found,说明cplexlml.m所在的目录没有加入 Matlab 路径,直接补addpath并savepath。 - 如果
which cplexlml能找到,但 Yalmip 还是报找不到,执行yalmiptest查看 CPLEX 测试状态。如果显示 failed,说明接口文件虽然可见,但它的自检失败了,通常是 MEX 文件无法加载动态库。 - 检查系统 PATH 中的 CPLEX bin 目录是否存在且指向当前版本。如果旧版本残留路径在前,Matlab 可能加载了旧版 DLL,导致接口初始化失败。
- 还有一种情况:你之前运行过
setuptbx,之后又移动了 CPLEX 安装目录。这时必须重新运行setuptbx,因为编译时生成的临时文件和路径信息已经失效。
6.2 场景二:版本不匹配的库冲突
这种坑更隐蔽。假设你的 Matlab 是 R2022a,CPLEX 是 20.1.0,可能一切正常;但如果换成 R2024a 加 CPLEX 20.1.0,接口加载时可能报:
Error using cplexlml Unable to load mex file...原因就是 CPLEX 的 MEX 接口是基于某个特定 Matlab 版本编译的(比如 R2019b 或 R2020a),新的 Matlab 版本号对不上,就拒绝加载。这类问题最稳妥的方式是查看 CPLEX 官方支持的 Matlab 版本列表,或者换用与 Matlab 版本更匹配的 CPLEX 版本。一般来说,CPLEX 12.9 对应较老的 Matlab,CPLEX 20.1 对应 R2019b 到 R2022b 区间,CPLEX 22.1 才开始支持 R2023a 以上。
这里给一个我自己的判断方法:与其追新 Matlab,不如锁定一个和 CPLEX 官方测试过的稳定组合。优化计算的核心是求解器能力,Matlab 界面新一点旧一点对实际影响不大。用 R2021b 或 R2022b 配 CPLEX 20.1,是很稳的配置。
6.3 场景三:已经装了Gurobi,再装CPLEX冲突吗
这个也是高频提问。答案是:不冲突,而且很多人都这么干。Yalmip 同时支持多个求解器,可以通过sdpsettings('solver', 'cplex')或sdpsettings('solver', 'gurobi')随时切换,两者共存不会产生文件冲突。
唯一要注意的是,如果你在装 CPLEX 之前已经装过 Gurobi,并且把 Gurobi 的matlab目录也加入了 Matlab 路径,那么which cplex和which gurobi都应该能正常返回。跑算例时如果不指定 solver,Yalmip 会按自己的优先级挑选一个已经识别且适合问题类型的求解器,通常也会给 CPLEX 或 Gurobi 较高的优先级。
另外,Yalmip 识别求解器的顺序取决于yalmiptest的检测结果,以及你addpath这些接口目录的先后顺序。如果希望强制某一题用 CPLEX,就在调用optimize时显式传入sdpsettings('solver','cplex'),这是最可靠的做法,不要指望 Yalmip“自动”选到你想用的求解器。
7. 周边连带问题的处理
7.1 装了多个Matlab版本怎么办
有些人的机器上既有 MATLAB R2020a 又有 R2022b,甚至还有学生版。这种情况下,CPLEX 的setuptbx必须针对每个版本的 Matlab 分别执行一次。因为每个版本的 Matlab 有各自独立的pathdef.m路径文件,不会互相同步。你在这个版本里加了路径,另一个版本不会自动知道。
我的建议是:不要给所有 Matlab 版本都配 Yalmip 和 CPLEX,锁定一个作为“工作版本”,其余版本保持默认设置。这样做的好处是排查问题更省心——当你发现一个配方在某个版本上稳定,就固定在那里,别让它被其他版本的环境折腾乱了。
7.2 .mat文件打不开,可以怎么查看内容
有些新接触 Matlab 的朋友会问:别人给的模型数据是.mat文件,但手头 Matlab 安装不完整,打不开怎么办。这里给两条路:
- 如果机器上有 Python,直接用
scipy.io.loadmat就能读.mat文件,缺点是对于新版 Matlab 的-v7.3格式支持不太好。要是遇到读不了的情况,可以在 Matlab 里用save('file.mat','-v7')重新保存为旧版格式。 - 如果不想装 Python,用 Octave 也可以打开大多数
.mat文件,但 Octave 对某些新版工具函数支持有限。
不过这些都是旁支,既然主线已经装好了 Yalmip 和 CPLEX,直接用 Matlab 打开.mat就是最顺手的方式。
整个流程走下来,我的体感是:这套三件套的环境搭建,难度不在于任何一个软件的安装,而在于版本匹配和路径配置的细节。只要按照上面的顺序来——先选好版本,再装 Matlab,配置 Yalmip,安装 CPLEX,绑定接口,验证诊断,跑算例——基本可以一次通过。真遇到问题,也把它当作理解优化工具链内部机制的一次机会,排查过程本身比结果更能帮你搞懂 Yalmip 和 CPLEX 是怎么协作的。