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.

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

To generate code using the rosenbrockwithgrad objective function, create a file named test_rosen.m containing this code:

function [x,fval] = test_rosen
opts = optimoptions('fmincon','Algorithm','sqp');
[x fval] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],[-3,-3],[3,3],[],opts)

Generate code for the test_rosen file.

codegen -config:mex test_rosen

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 test_rosen_mex. The results are the following or similar:

x =

    1.0000    1.0000


fval =

   1.3346e-11


ans =

    1.0000    1.0000

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.DynamicMemoryAllocation = 'Off';

Tighten the bounds on the problem from [-3,3] to [-2,2]. Also, set a looser optimality tolerance than the default 1e-6.

function [x,fval] = test_rosen2
opts = optimoptions('fmincon','Algorithm','sqp',...
    'OptimalityTolerance',1e-5);
[x fval eflag output] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],...
    [-2,-2],[2,2],[],opts)

Generate code for the test_rosen2 file.

codegen -config cfg test_rosen2

Run the resulting code.

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

This solution is almost as good as the previous solution, with the fval output around 2e-11 compared to the previous 1e-11.

Try limiting the number of allowed iterations to half of those taken in the previous computation.

function [x,fval] = test_rosen3
options = optimoptions('fmincon','Algorithm','sqp',...
    'MaxIterations',20);
[x fval eflag output] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],...
    [-2,-2],[2,2],[],options)

Run test_rosen3 in MATLAB.

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

With this severe iteration limit, fmincon does not reach a good solution. The tradeoff between accuracy and speed can be difficult to manage.

To save function evaluations and possibly increase accuracy, use the example built-in derivatives of the example by setting the SpecifyObjectiveGradient option to true.

function [x,fval] = test_rosen4
options = optimoptions('fmincon','Algorithm','sqp',...
    'SpecifyObjectiveGradient',true);
[x fval eflag output] = fmincon(@rosenbrockwithgrad,[-1,1],[],[],[],[],...
    [-2,-2],[2,2],[],options)

Generate code for test_rosen4 using the same configuration as in test_rosen2.

codegen -config cfg test_rosen4

Run the resulting code.

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

Compared to test_rosen2, the number of iterations is the same at 40, but the number of function evaluations is lower at 113 instead of 155. The result has a better (lower) objective function value of 3e-20 compared to 2e-11.

See Also

| |

Related Topics