优化代码生成基础知识
为 fmincon 生成代码
此示例说明如何为 fmincon 优化求解器生成代码。代码生成需要 MATLAB® Coder™ 许可证。有关代码生成要求的详细信息,请参阅 fmincon 背景中的代码生成。
该示例使用以下简单的目标函数。要在您自己的测试中使用此目标函数,请将代码复制到名为 rosenbrockwithgrad.m 的文件中。将此文件保存到您的 MATLAB 路径中。
function [f,g] = rosenbrockwithgrad(x) % Calculate objective f f = 100*(x(2) - x(1)^2)^2 + (1 - x(1))^2; if nargout > 1 % gradient required g = [-400*(x(2) - x(1)^2)*x(1) - 2*(1 - x(1)); 200*(x(2) - x(1)^2)]; end
要使用 rosenbrockwithgrad 目标函数生成代码,请创建一个名为 test_rosen.m 的文件,其中包含调用 rosenbrockwithgrad 的代码。您必须设置选项才能使用 "sqp" 算法。另外,请将 UseCodegenSolver 选项设置为 true,以便可以在 MATLAB 中使用生成的相同代码来验证结果。
function [x,fval] = test_rosen opts = optimoptions("fmincon",... Algorithm="sqp",UseCodegenSolver=true); [x fval] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],[-3,-3],[3,3],[],opts)
为 test_rosen 文件生成代码。
codegen -config:mex test_rosen
一段时间后,codegen 会创建一个名为 test_rosen_mex.mexw64 的 MEX 文件(文件扩展名因系统而异)。您可以通过输入 test_rosen_mex 来运行生成的 C 代码。结果如下或与以下内容类似:
x =
1.0000 1.0000
fval =
1.3346e-11
ans =
1.0000 1.0000修改示例以提高效率
按照实时应用的优化代码生成中的一些建议,设置生成代码的配置,以减少检查并使用静态内存分配。
cfg = coder.config('mex'); cfg.IntegrityChecks = false; cfg.SaturateOnIntegerOverflow = false; cfg.DynamicMemoryAllocation = 'Off';
将问题的边界从 [-3,3] 收紧到 [-2,2]。此外,设置比默认值 1e-6 更宽松的最优性容差。
function [x,fval] = test_rosen2 opts = optimoptions("fmincon",... Algorithm="sqp",UseCodegenSolver=true,... OptimalityTolerance=1e-5); [x fval eflag output] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],... [-2,-2],[2,2],[],opts)
为 test_rosen2 文件生成代码。
codegen -config cfg test_rosen2
运行生成的代码。
test_rosen2_mex
x =
1.0000 1.0000
fval =
2.0057e-11
eflag =
2
output =
struct with fields:
iterations: 40
funcCount: 155
algorithm: 'sqp'
constrviolation: 0
stepsize: 5.9344e-08
lssteplength: 1
ans =
1.0000 1.0000此解几乎与之前的解一样好,其 fval 输出约为 2e-11,而之前为 1e-11。
尝试将允许的迭代次数限制为上一次计算所需迭代次数的一半。
function [x,fval] = test_rosen3 options = optimoptions("fmincon",... Algorithm="sqp",UseCodegenSolver=true,... MaxIterations=20); [x fval eflag output] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],... [-2,-2],[2,2],[],options)
在 MATLAB 中运行 test_rosen3。
test_rosen3
x =
0.2852 0.0716
fval =
0.5204
eflag =
0
output =
struct with fields:
iterations: 20
funcCount: 91
algorithm: 'sqp'
message: '↵Solver stopped prematurely.↵↵fmincon stopped because it exceeded the iteration limit,↵options.MaxIterations = 2.000000e+01.↵↵'
constrviolation: 0
stepsize: 0.0225
lssteplength: 1
firstorderopt: 1.9504
ans =
0.2852 0.0716在这种严格的迭代限制下,fmincon 不能得到良好解。准确性和速度之间的权衡难以把握。
为了保存函数计算并尽可能提高准确性,通过将 SpecifyObjectiveGradient 选项设置为 true 来使用该示例的内置导数。
function [x,fval] = test_rosen4 options = optimoptions("fmincon",... Algorithm="sqp",UseCodegenSolver=true,... SpecifyObjectiveGradient=true); [x,fval,eflag,output] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],... [-2,-2],[2,2],[],options)
使用与 test_rosen2 中相同的配置为 test_rosen4 生成代码。
codegen -config cfg test_rosen4
运行生成的代码。
test_rosen4_mex
x =
1.0000 1.0000
fval =
3.3610e-20
eflag =
2
output =
struct with fields:
iterations: 40
funcCount: 113
algorithm: 'sqp'
constrviolation: 0
stepsize: 9.6356e-08
lssteplength: 1
ans =
1.0000 1.0000与 test_rosen2 相比,迭代次数同为 40,但函数计算次数降低为 113,而不是 155。结果具有容差为 3e-20 的更好(更低)的目标函数值,而之前为 2e-11。
另请参阅
fmincon | codegen (MATLAB Coder) | optimoptions