Code Generation for Optimization Basics
Generate Code for fmincon
This example shows how to generate code for the fmincon
optimization solver. Code generation requires a MATLAB®
Coder™ license. For details of code generation requirements, see Code Generation in fmincon Background.
The example uses the following simple objective function. To use this objective
function in your own testing, copy the code to a file named
rosenbrockwithgrad.m. Save the file on your MATLAB path.
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
To generate code using the rosenbrockwithgrad objective
function, create a file named test_rosen.m containing code that
calls rosenbrockwithgrad. You must set options to use the
"sqp" algorithm. To use the provided gradient of the
objective function, set the SpecifyObjectiveGradient option to
true. To verify the results in MATLAB using the same code as is generated, set the
UseCodegenSolver option to 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
Test this code in 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.'Generate code for the test_rosen file.
codegen -config:mex test_rosen -args {x0,lb,ub}
After some time, codegen creates a MEX file named
test_rosen_mex.mexw64 (the file extension will vary,
depending on your system). You can run the resulting C code by entering
[x,fval] = test_rosen_mex(x0,lb,ub)
The results are the following or similar:
x =
1.0000 1.0000
fval =
5.6977e-20The result using code generation is nearly the same as the result of running the code in MATLAB.
Modify Example for Efficiency
Following some of the suggestions in Optimization Code Generation for Real-Time Applications, set the configuration of the generated code to have fewer checks and to use static memory allocation.
cfg = coder.config("mex");
cfg.IntegrityChecks = false;
cfg.SaturateOnIntegerOverflow = false;
cfg.EnableDynamicMemoryAllocation = false;First check the average time to run the problem using generated code with the default mex code generation options.
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
Generate code using the cfg configuration.
codegen -config cfg test_rosen -args {x0,lb,ub}
Check the average time to run the problem using generated code with the
cfg configuration.
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
The average time to run the generated code is about 2/3 of the previous time.
Try limiting the number of allowed iterations to 20, which is about half of those taken in the first computation.
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
Run test_rosen3 in MATLAB.
[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.'With this severe iteration limit, fmincon does not reach a
good solution. The tradeoff between accuracy and speed can be difficult to
manage.
Generate Code for Single-Precision Computations
To generate code for single-precision data, the only change you need is to pass all data as single-precision.
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-10The resulting fval is of the order 1e-10 rather than the
double-precision result that is on the order of 1e-20, but other than that the
procedure is identical.
Generate code and try the single-precision computation again.
codegen -config:mex test_rosen -args {x0,lb,ub}
Pass the single-precision input values to the generated function.
[x,fval] = test_rosen_mex(x0,lb,ub)
x =
1×2 single row vector
0.9999 0.9998
fval =
single
1.1349e-08The generated code gives results that are close to the MATLAB results.
See Also
fmincon | codegen (MATLAB Coder) | optimoptions