メインコンテンツ

最適化式の静的解析

静的解析とは

静的解析は、関数を解析し、効率的にコード化できる規則性の有無を探すプロセスです。特に、fcn2optimexpr は、for ループを効率的に符号化しようとするために静的解析を実行します。これを行うために、fcn2optimexpr ではループが別個の関数内になければなりません。入れ子形式の for ループは 1 つの関数内に入れることができます。例静的解析のための for ループの作成では、for ループを別個の関数に容易に変換する方法を示します。例静的解析のための for ループの制約の変換では、別個の関数の制約に for ループを作成する方法を示します。

静的解析の一環として、どの関数が数値データのみで動作するか、およびどれが最適化変数に依存するかを特定します。最適化変数に依存しない関数の場合、fcn2optimexpr はサポートされていない関数を自動的にラップし、関数の残りの部分が自動微分を利用できるようにします。このサポートされていない関数をラップしたものは、最適化変数に関してゼロ勾配をもつと仮定されます。たとえば、関数 ceil はサポートされていません (最適化変数および式でサポートされる演算を参照してください)。ただし、次の関数では、ceil が最適化変数ではなくループ変数のみで動作するため、fcn2optimexpr は自動微分をサポートする式を返します。

function [expr, val] = forloop(x, y)
N = numel(y);
expr = y + 2;
val = zeros(N,1);
for i = 1:N
    val(i) = ceil(sqrt(i+1));
    expr(i) = expr(i) + val(i)*x;
end
end

静的解析の制限

静的解析は次をサポートしていません。

  • cell 配列

  • セル インデックス付け

  • ドット インデックス付け

  • 無名関数

  • breakcontinue、および return ステートメント

  • クラス

  • 入れ子関数

  • グローバル変数

  • 永続変数

  • 名前と値の引数

  • parfor

  • switch ステートメント

  • try-catch ブロック

  • while ステートメント

  • if ステートメント

  • string インデックス付け

  • 非数値のループ範囲

  • 複数の左辺への代入 — 静的解析は、次のような複数の左辺を含む代入をサポートしていません。

    [a,b] = peaks(5);
  • ループ内で縮小または拡大する式 — 静的解析は、式の大きさを変化させる for ループをサポートしていません。すべての式を事前に割り当てることをお勧めします。たとえば、次のループは静的解析でサポートされていません。

    for i = 1:10
        x = [x fun(i)]; % x grows by concatenation
    end
    %%
    temp = x.^2;
    for i = 1:N
        temp(i+1) = temp(i) + x.*i; % temp grows by indexing
    end
    %%
    for i = 2:N
        expr(2:i) = expr(1:i-1) - i*x^2;
    % Vectors 2:i and 1:i-1 change size at each iteration
    end
    %%
    for i = N:-1:1
        expr(i) = []; % expr shrinks at each iteration
    end
  • 静的解析は、main 関数によって呼び出されるサブ関数には適用されません。静的解析では、サブ関数をそのまま使用できる場合もありますが、サブ関数をソフトウェアでブラック ボックスとしてラップすることが必要になる場合もあります。この要件は、一般に、サブ関数でインデックス付けを使用しており、式のサイズが変わる可能性がある場合に当てはまります。

    たとえば、同じ式を評価する 2 つの手法について考えてみます。1 つはサブ関数でインデックス付けを使用する手法で、したがって静的解析は使用できません。もう 1 つは main 関数でインデックス付けを使用する手法であり、静的解析を使用します。

    N = 5000;
    q = optimvar("q",5,N);
    tic
    ceqstat1 = fcn2optimexpr(@nonlconNotUseStatic,q,N);
    out1 = evaluate(ceqstat1,s);
    toc
    Elapsed time is 32.319667 seconds.
    tic
    ceqstat2 = fcn2optimexpr(@nonlconUseStatic,q,N);
    out2 = evaluate(ceqstat2,s);
    toc
    Elapsed time is 10.332486 seconds.
    function dyneval = nonlconUseStatic(q,N)
    
    dyneval = zeros(2,N,"like",q);
    for i = 1:N  
        tmp = q(:,i);
        tmp2 = sin(tmp([1 5]));
        dyneval(:,i) = tmp2; 
    end
    
    end
    
    function dyneval = nonlconNotUseStatic(q, N)
    
    dyneval = zeros(2,N,"like",q);
    for i = 1:N
        dyneval(:,i) = myfun(q(:,i)); 
    end
    
    end
    
    function y = myfun(x)
    y = sin(x([1 5]));
    end

    静的解析を使用した手法の方がはるかに高速に実行されます。

また、静的解析では計算をしない関数を無視することができます。アルゴリズムにはこのような特徴があるため、次のような結果になります。

  • pause ステートメントは無視されます。

  • 結果に影響しないグローバル変数は無視される可能性があります。たとえば、グローバル変数を使用して関数の実行回数をカウントすると、誤った回数を得る場合があります。

  • rand または rng の呼び出しが関数に含まれている場合、関数は 1 回目のみ呼び出しを実行すると考えられますが、その後の呼び出しでは乱数ストリームが設定されません。

  • plot を呼び出しても、すべての反復で Figure が更新されない場合があります。

  • mat ファイルまたはテキスト ファイルへのデータの保存は、すべての反復で実行されない場合があります。

計算をしない関数が想定したとおりに動作するには、fcn2optimexprAnalysis 名前と値の引数を "off" に設定します。

参考

トピック