优化代码生成基础知识
为 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 end
要使用 rosenbrockwithgrad 目标函数生成代码,请创建一个名为 test_rosen.m 的文件,其中包含调用 rosenbrockwithgrad 的代码。您必须设置选项才能使用 "sqp" 算法。若要使用目标函数的预设梯度,请将 SpecifyObjectiveGradient 选项设置为 true。若要使用与生成代码相同的代码来验证 MATLAB 中的结果,请将 UseCodegenSolver 选项设置为 true。
function [x,fval,exitflag,output] = test_rosen(x0,lb,ub) opts = optimoptions("fmincon",... Algorithm="sqp",SpecifyObjectiveGradient=true,... UseCodegenSolver=true); [x,fval,exitflag,output] = fmincon(@rosenbrockwithgrad,... x0,[],[],[],[],lb,ub,[],opts); end
在 MATLAB 中测试这段代码。
x0 = [-1 1]; lb = [-3 -3]; ub = [3 3]; [x,fval,exitflag,output] = test_rosen(x0,lb,ub)
Local minimum found that satisfies the constraints.
Optimization completed because the objective function is non-decreasing in
feasible directions, to within the value of the optimality tolerance,
and constraints are satisfied to within the value of the constraint tolerance.
x =
1.0000 1.0000
fval =
5.6989e-20
exitflag =
1
output =
struct with fields:
iterations: 13
funcCount: 49
algorithm: 'sqp'
constrviolation: 0
stepsize: 2.1608e-06
lssteplength: 1
firstorderopt: 8.9962e-09
message: 'Local minimum found that satisfies the constraints.↵↵Optimization completed because the objective function is non-decreasing in ↵feasible directions, to within the value of the optimality tolerance,↵and constraints are satisfied to within the value of the constraint tolerance.'为 test_rosen 文件生成代码。
codegen -config:mex test_rosen -args {x0,lb,ub}
一段时间后,codegen 会创建一个名为 test_rosen_mex.mexw64 的 MEX 文件(文件扩展名因系统而异)。您可以通过输入以下内容来运行生成的 C 代码:
[x,fval] = test_rosen_mex(x0,lb,ub)
结果如下或与以下内容类似:
x =
1.0000 1.0000
fval =
5.6977e-20使用代码生成得到的结果与在 MATLAB 中运行代码的结果几乎相同。
修改示例以提高效率
按照实时应用的优化代码生成中的一些建议,设置生成代码的配置,以减少检查并使用静态内存分配。
cfg = coder.config("mex");
cfg.IntegrityChecks = false;
cfg.SaturateOnIntegerOverflow = false;
cfg.EnableDynamicMemoryAllocation = false;首先,使用默认的 mex 代码生成选项,检查使用生成代码运行该问题的平均耗时。
N = 10000; % Run 10,000 trials pp = zeros(N,1); for i=1:N tic [x,fval,exitflag,output] = test_rosen_mex(x0,lb,ub); pp(i) = toc; end m = mean(pp)
m = 8.9106e-05
使用 cfg 配置生成代码。
codegen -config cfg test_rosen -args {x0,lb,ub}
检查使用 cfg 配置生成代码运行该问题的平均耗时。
N = 10000; % Run 10,000 trials pp = zeros(N,1); for i=1:N tic [x,fval,exitflag,output] = test_rosen_mex(x0,lb,ub); pp(i) = toc; end m = mean(pp)
m = 6.0810e-05
运行生成代码所需的平均时间约为之前所需时间的 2/3。
试着将允许的迭代次数限制为 20 次,这大约是第一次计算中迭代次数的一半。
function [x,fval,exitflag,output] = test_rosen3(x0,lb,ub) options = optimoptions("fmincon",... Algorithm="sqp",SpecifyObjectiveGradient=true,... UseCodegenSolver=true,MaxIterations=20); [x,fval,exitflag,output] = fmincon(@rosenbrockwithgrad,... x0,[],[],[],[],lb,ub,[],options); end
在 MATLAB 中运行 test_rosen3。
[x,fval,exitflag,output] = test_rosen3(x0,lb2,ub2)
x =
0.2852 0.0716
fval =
0.5204
exitflag =
0
output =
struct with fields:
iterations: 20
funcCount: 70
algorithm: 'sqp'
constrviolation: 0
stepsize: 0.0225
lssteplength: 1
firstorderopt: 1.9507
message: 'Solver stopped prematurely.↵↵fmincon stopped because it exceeded the iteration limit,↵options.MaxIterations = 2.000000e+01.'在这种严格的迭代限制下,fmincon 不能得到良好解。准确性和速度之间的权衡难以把握。
生成单精度计算的代码
要生成针对单精度数据的代码,您只需将所有数据作为单精度数据传递即可。
x0 = single([-1 1]); lb = single([-3 -3]); ub = single([3 3]); [x,fval] = test_rosen(x0,lb,ub)
Local minimum found that satisfies the constraints.
Optimization completed because the objective function is non-decreasing in
feasible directions, to within the value of the optimality tolerance,
and constraints are satisfied to within the value of the constraint tolerance.
x =
1×2 single row vector
1.0000 1.0001
fval =
single
9.6799e-10所得的 fval 量级为 1e-10,而非量级为 1e-20 的双精度结果,但除此之外,该过程完全相同。
生成代码,然后再次尝试单精度计算。
codegen -config:mex test_rosen -args {x0,lb,ub}
将单精度值传递给生成的函数。
[x,fval] = test_rosen_mex(x0,lb,ub)
x =
1×2 single row vector
0.9999 0.9998
fval =
single
1.1349e-08生成代码得出的结果与 MATLAB 的结果非常接近。
另请参阅
fmincon | codegen (MATLAB Coder) | optimoptions