メインコンテンツ

最適化のためのコード生成の基本

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 目的関数を使用してコードを生成する場合は、rosenbrockwithgrad を呼び出すコードを含む test_rosen.m という名前のファイルを作成します。"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-20 ではなく 1e-10 になりますが、それ以外の手続きは同じです。

もう一度、コードを生成して単精度計算を試します。

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 の結果に近い結果が生成コードで得られます。

参考

| (MATLAB Coder) |

トピック