关于 matlab-solve-optimization
这是一个指导 MATLAB 优化全流程的技能,涵盖问题建模、求解器选择与配置,以及结果验证。面向使用 MathWorks 优化工具构建或调试数值优化工作流的用户。
主要功能
- 01分类并构建优化问题
- 02按问题结构选择求解器
- 03配置并核验求解器选项
- 04检查梯度与初始可行性
- 05验证结果并诊断收敛情况
适用场景
- 01为带约束模型选择求解器
- 02排查收敛失败或结果不佳
- 03优化前检查梯度
- 04验证解与约束满足情况
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-solve-optimization安装后在 Claude Code 中输入/matlab-solve-optimization确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
manifest.yamlreferences/classify.mdfcn2optimexpr-guide.mdimproving-results.mdperformance-levers.mdproblem-based-guide.mdsolver-tuning.mdvalidation-checklist.md详细介绍
MATLAB 优化工作流
指导完整的优化生命周期:对问题进行分类、建立问题模型、选择并配置求解器,以及验证结果。
适用场景
- 用户正在 MATLAB 中定义优化问题(变量、目标、约束)
- 用户询问
optimproblem、optimvar、optimconstr、optimexpr或fcn2optimexpr - 用户正在选择或配置求解器(
optimoptions、算法选择、调优) - 用户正在解读结果、调试收敛问题或检查 exitflags
- 用户正在选择基于问题的方法还是基于求解器的方法
- 用户正在编写包含决策变量或约束
for循环的优化代码
不适用场景
- 用户要解决的问题不需要数值优化求解器(例如,在数组或表中查找最小值)
- 用户正在处理非优化类 MATLAB 代码(数据分析、绘图、信号处理)
- 用户使用第三方优化工具箱(非 MathWorks)
- 用户正在用
solve(eqns, vars)求解符号方程、ODE 系统或线性方程组(A\b)
阶段 1:分类并建立问题模型
1.1 对问题进行分类
在建立问题模型之前,先确定问题类别——它决定了要使用的求解器、能提供何种保证(全局还是局部),以及是否应使用特定领域工具替代通用流程。
请参阅 references/classify.md,其中包含类别→求解器→保证对照表、凸性快速检查,以及“隐藏的更简单类别”启发式方法。关键操作:
- 在退回使用
optimproblem之前,检查是否存在专用领域工具 - 留意隐藏的更简单类别(伪装成 NLP 的平方和问题、被忽略的线性结构)
- 对于 QP,检查
eig(H)——非凸 QP 无法可靠地使用quadprog - 留意隐藏的非光滑性:
max、min、abs、sort、if/分支,或平方 2 范数以外的范数
1.2 选择方法
默认使用基于问题的方法,适用于定义清晰易读、N-D 建模,以及所有 LP、QP、锥规划和混合整数问题(除非系数已经是矩阵-向量形式)。基于问题的方法提供自动微分,且更不容易出错。
即使自动微分受阻(例如,目标函数中使用了 ode45),fcn2optimexpr 仍可将函数封装为黑盒——基于问题的方法依然有用。
只有在以下情况之一成立时,才改用基于求解器的方法:
| 适合使用基于求解器的方法的情况…… | 原因 |
|---|---|
可直接映射到求解器 API——只有一个向量 x,且目标函数已编码并提供精确梯度/海森矩阵 | 抽象没有带来好处;基于求解器的方法更直接 |
| 构建基于问题的表达式所产生的开销超过计算本身 | 避免跟踪/转换开销 |
需要基于问题的方法未提供的求解器功能(CheckpointFile、精确海森矩阵、自定义 OutputFcn) | 只能通过基于求解器的调用使用 |
| 嵌入式部署需要生成 C 代码 | 基于问题的方法不支持代码生成 |
方法之间的转换: prob2struct(prob) 可将基于问题的形式转换为基于求解器的形式,用于部署或提升性能。
参考资料:
- 基于问题的方法:references/problem-based-guide.md
- 基于求解器的方法(类别→求解器映射):references/classify.md
1.3 建立问题模型
基于问题的方法标准模板:
% 1. Define decision variables
x = optimvar("x", N, LowerBound=lb, UpperBound=ub);
% 2. Create problem
prob = optimproblem("Objective", sum(x,"all"));
% 3. Add constraints
prob.Constraints.linear = A*x <= b;
prob.Constraints.nonlinear = fcn2optimexpr(@myNonlinFcn, x) <= rhs;
% 4. Set initial guess (must be struct with field names matching optimvar names)
x0.x = initialValues;
% 5. Solve
[sol, fval, exitflag, output] = solve(prob, x0);
基于求解器的方法的主要区别:
- 初始猜测是数值向量,而不是结构体
- 需要手动管理变量索引(扁平向量
x) - 为获得最佳性能,需手动提供梯度(
SpecifyObjectiveGradient=true) - 线性/二次求解器要求提供显式系数矩阵
1.4 在起始点进行验证
调用任何求解器之前,先在 x0 处计算目标函数和约束,以便尽早发现符号、大小或 NaN 错误:
% Problem-based
fval0 = evaluate(prob.Objective, x0);
assert(isfinite(fval0), 'Objective is not finite at x0');
infeas0 = infeasibility(prob.Constraints, x0);
fprintf('Max infeasibility at x0: %.3e\n', max(infeas0));
对于基于求解器的情形,直接调用 fun(x0) 和 nonlcon(x0),并确认输出有限且尺寸正确。如果提供了梯度,此时运行 checkGradients。
阶段 2:选择并配置求解器
2.1 选择最适合问题的求解器
选择与问题结构相匹配且适用范围最窄的求解器。 如果有更具体的求解器适用,不要默认使用 fmincon 或启发式全局求解器。
主要选择规则:
- 始终优先选择:
linprog>quadprog>coneprog>lsqlin>lsqnonlin>fmincon> 全局求解器 - 安装了 Optimization Toolbox 时,始终优先使用
fminunc而不是fminsearch - 对于最小二乘问题,始终优先使用
lsqnonlin/lsqcurvefit而不是fmincon - 对于带边界或线性约束的线性最小二乘问题,始终优先使用
lsqlin而不是lsqnonlin - 当梯度不可用或不可靠,且问题的计算成本并非极高时,使用
patternsearch - 当每次求值耗时超过 15–20 秒时,使用
surrogateopt - 对于近似线性的 MIP,应进行线性化并使用
intlinprog,而不是调用 Global Optimization 求解器 - 对于机组组合/二进制运行模式,使用
intlinprog保留混合整数形式
完整的类别→求解器表请参阅 references/classify.md。
2.2 验证选项——切勿猜测
使用求解器选项前,务必验证其是否有效。 选项会随 MATLAB 版本而变化,臆造选项会导致运行时错误。
% Verify options for a solver
opts = optimoptions('solvername')
编写选项代码前,运行 optimoptions('solvername'),查看用户已安装版本支持的所有有效选项。
2.3 验证梯度(如果已提供)
如果提供了解析梯度(SpecifyObjectiveGradient=true),请在求解前验证:
[valid, err] = checkGradients(@myObjective, x0, Display="on");
对于约束梯度:checkGradients(@myConstraints, x0, IsConstraint=true)。
2.4 并行化(如果计算成本高)
如果求解器支持 UseParallel,且 Parallel Computing Toolbox 可用:
ver('parallel') % Check for PCT
options = optimoptions('solvername', UseParallel=true);
支持 UseParallel 的求解器:fmincon、fminunc、lsqnonlin、lsqcurvefit、patternsearch、surrogateopt、ga、particleswarm、paretosearch、gamultiobj。
对于以下求解器,不要建议使用 UseParallel:quadprog、intlinprog、fminsearch、linprog、lsqlin。
2.5 性能(正确性得到确认后)
如果求解结果正确但速度太慢,请参阅 references/performance-levers.md。主要优化手段:解析梯度、稀疏模式、热启动、代码生成。仅在阶段 3 确认正确性后再应用——任何性能变更后都要重新验证。
参考: references/solver-tuning.md,了解各求解器的算法和调优指南。
阶段 3:验证结果
3.1 基本验证(始终包含)
每次编写调用求解器的代码时,都要添加基本输出验证:
[sol, fval, exitflag, output] = solve(prob, x0);
% Check convergence
if exitflag > 0
fprintf('Optimization converged: %s\n', output.message);
else
warning('Optimization did not converge (exitflag = %d): %s\n', exitflag, output.message);
end
% Report key metrics
fprintf('Objective value: %.6f\n', fval);
fprintf('Iterations: %d\n', output.iterations);
if isfield(output, 'constrviolation')
fprintf('Constraint violation: %d\n', output.constrviolation);
end
各求解器的 exitflag 含义详见 references/validation-checklist.md。
3.2 扩展验证
约束违反情况(基于问题的形式):
[allsat, sat] = issatisfied(prob, sol);
if ~allsat
conNames = fieldnames(prob.Constraints);
for i = 1:numel(conNames)
infeas = infeasibility(prob.Constraints.(conNames{i}), sol);
if any(infeas > 0)
fprintf('Constraint "%s" violated by %.3e\n', conNames{i}, max(infeas));
end
end
end
最优性条件(仅适用于基于梯度的求解器——patternsearch、ga、particleswarm、surrogateopt 除外):
if isfield(output, 'firstorderopt')
fprintf('First-order optimality: %.6e\n', output.firstorderopt);
if output.firstorderopt > 1e-3
warning('First-order optimality measure is large — solution may not be optimal.\n');
end
end
3.3 调试失败或效果不佳的求解
当 exitflag <= 0 或收敛效果不佳时,请按照 references/improving-results.md 中的结果改进清单进行检查:
- 检查问题构造 — 约束是否可行?边界是否一致?目标函数在 x0 处是否定义良好?
- 检查缩放 — 将变量缩放到 O(1);如果目标函数与约束的量级相差悬殊,请重新缩放;如果有限差分梯度不准确,请使用
FiniteDifferenceType='central' - 尝试不同的算法 — 调整
options.Algorithm,增大MaxIterations/MaxFunctionEvaluations,调整容差,为启发式求解器设置HybridFcn - 尝试不同的初始点 — 使用
MultiStart、GlobalSearch或surrogateopt/ga进行全局优化
调试原则:
- 从最小规模开始。 将问题缩减到 2-3 个变量。在小型示例问题中查找错误只需几分钟,而在完整规模的问题中则可能要花费几小时。
- 一次只做一项更改,并确保更改有症状作为依据。
- 设定停止并询问的界限。 出现以下情况时,停止编码并与用户沟通:超过 3 轮仍无改善;调整选项超过 2 次但诊断结果没有变化;或者即使在小型示例问题中也无法在 x0 处获得有限的目标函数值。
3.4 特定应用的可视化
| 问题领域 | 建议绘图 |
|---|---|
| 最优控制 / 导航 | 状态随时间变化的轨迹、控制输入曲线、相图 |
| 调度 / 分配 | 甘特图、资源随时间的利用率 |
| 设计优化 | 标出最优点的等高线图、灵敏度图 |
| 参数估计 / 拟合 | 残差图、拟合曲面与数据的对比 |
| 投资组合 / 配置 | 配置柱状图、有效前沿图 |
常见问题
问题构造
- 初始猜测必须是一个结构体,其字段名称必须与
optimvar名称完全一致。不能是扁平向量。 - 对基于问题的建模,不要在选项中设置
SpecifyObjectiveGradient或SpecifyConstraintGradient— 自动微分会在内部处理梯度。 - 对于多维问题,请使用 N-D
optimvar。 不要在循环中创建标量变量。 - 使用
optimconstr(N)预分配约束数组。 不要在循环中进行拼接。 - 每个函数只调用一次
fcn2optimexpr,不要在循环中调用。 请参阅 references/fcn2optimexpr-guide.md。 - 在经过追踪的函数内部进行预分配时使用
"like",以保留 AD 类型:zeros(n,1,"like",x)。
求解器配置
- 切勿凭记忆猜测选项名称。 始终使用
optimoptions('solvername')进行验证。 - 对于
patternsearch,不要将MeshTolerance调得过严。 - 对于
intlinprog,若要提前停止,不要将AbsoluteGapTolerance/RelativeGapTolerance设得过高 — 应使用时间/节点限制。 - 确保容差远高于机器精度。 除非有特殊要求,否则使用
1e-6到1e-8的范围。
验证
- 无约束求解器不存在
output.constrviolation。 始终使用isfield检查。 - 对于无导数求解器,不要检查
output.firstorderopt。应改为检查求解器专属指标(output.meshsize、output.stallgenerations)。 infeasibility()用于单个约束,而非整个问题。 使用issatisfied(prob, sol)进行整体检查。- 对于
fmincon,若exitflag <= 0,请检查output.bestfeasible。 将其用作新一轮求解的初始点。
约定
- 除非存在特定阻碍,否则默认采用基于问题的方法。
- 可以进行向量化时,始终优先使用向量化,而不是循环。
- 使用
fcn2optimexpr封装复杂逻辑时,应将其封装在单个辅助函数中,而不是在循环内调用。 - 始终展示初始猜测值的设置。
- 依赖用户提供的函数时,将代码块标记为模板。
- 建议调整选项时,说明其中的权衡(速度与精度)。
- 不要过度调优:对于简单或规模较小的问题,默认设置通常已足够。
- 编写调用求解器的代码时,始终加入基本验证(检查 exitflag)。
- 调试时,应先检查问题的建模和缩放,再更改算法。
版权所有 2026 The MathWorks, Inc.